diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 48a6e8942a..a1b780849d 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -5,7 +5,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aes-soft 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "aesni 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ctr 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ctr 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "stream-cipher 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -329,8 +329,8 @@ dependencies = [ "isatty 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", "simplelog 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -427,7 +427,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ctr" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-cipher-trait 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -451,7 +451,7 @@ 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.25 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -703,7 +703,7 @@ dependencies = [ [[package]] name = "h2" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -804,13 +804,13 @@ dependencies = [ [[package]] name = "hyper" -version = "0.12.11" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "h2 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "h2 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -893,21 +893,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "jsonrpc-core" version = "9.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git#8d41129955e9abf08399cd052b4a6df4e0743ad6" +source = "git+https://github.com/paritytech/jsonrpc.git#207a277b098943864ecaf22dbab7a5e309866d6b" dependencies = [ "futures 0.1.25 (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.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-http-server" version = "9.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git#8d41129955e9abf08399cd052b4a6df4e0743ad6" +source = "git+https://github.com/paritytech/jsonrpc.git#207a277b098943864ecaf22dbab7a5e309866d6b" dependencies = [ - "hyper 0.12.11 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 9.0.0 (git+https://github.com/paritytech/jsonrpc.git)", "jsonrpc-server-utils 9.0.0 (git+https://github.com/paritytech/jsonrpc.git)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -918,17 +918,17 @@ dependencies = [ [[package]] name = "jsonrpc-macros" version = "9.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git#8d41129955e9abf08399cd052b4a6df4e0743ad6" +source = "git+https://github.com/paritytech/jsonrpc.git#207a277b098943864ecaf22dbab7a5e309866d6b" dependencies = [ "jsonrpc-core 9.0.0 (git+https://github.com/paritytech/jsonrpc.git)", "jsonrpc-pubsub 9.0.0 (git+https://github.com/paritytech/jsonrpc.git)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-pubsub" version = "9.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git#8d41129955e9abf08399cd052b4a6df4e0743ad6" +source = "git+https://github.com/paritytech/jsonrpc.git#207a277b098943864ecaf22dbab7a5e309866d6b" dependencies = [ "jsonrpc-core 9.0.0 (git+https://github.com/paritytech/jsonrpc.git)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -938,7 +938,7 @@ dependencies = [ [[package]] name = "jsonrpc-server-utils" version = "9.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git#8d41129955e9abf08399cd052b4a6df4e0743ad6" +source = "git+https://github.com/paritytech/jsonrpc.git#207a277b098943864ecaf22dbab7a5e309866d6b" dependencies = [ "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -954,7 +954,7 @@ dependencies = [ [[package]] name = "jsonrpc-ws-server" version = "9.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git#8d41129955e9abf08399cd052b4a6df4e0743ad6" +source = "git+https://github.com/paritytech/jsonrpc.git#207a277b098943864ecaf22dbab7a5e309866d6b" dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 9.0.0 (git+https://github.com/paritytech/jsonrpc.git)", @@ -1215,8 +1215,8 @@ dependencies = [ "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)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1275,7 +1275,7 @@ dependencies = [ "aes-ctr 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "asn1_der 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", - "ctr 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ctr 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "eth-secp256k1 0.5.7 (git+https://github.com/paritytech/rust-secp256k1)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p-core 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", @@ -1513,7 +1513,7 @@ dependencies = [ "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "multihash 0.8.1-pre (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "unsigned-varint 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1597,37 +1597,20 @@ dependencies = [ "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "node-executor 0.1.0", - "node-network 0.1.0", - "node-primitives 0.1.0", - "node-runtime 0.1.0", - "sr-primitives 0.1.0", - "substrate-cli 0.3.0", - "substrate-network 0.1.0", - "substrate-primitives 0.1.0", - "substrate-service 0.3.0", - "substrate-service-test 0.3.0", - "substrate-transaction-pool 0.1.0", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "node-consensus" -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.25 (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", "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rhododendron 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 0.1.0", "sr-primitives 0.1.0", - "srml-system 0.1.0", + "substrate-cli 0.3.0", "substrate-client 0.1.0", - "substrate-keyring 0.1.0", + "substrate-consensus-aura 0.1.0", + "substrate-network 0.1.0", "substrate-primitives 0.1.0", + "substrate-service 0.3.0", + "substrate-service-test 0.3.0", "substrate-transaction-pool 0.1.0", "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1660,21 +1643,6 @@ dependencies = [ "wabt 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "node-network" -version = "0.1.0" -dependencies = [ - "futures 0.1.25 (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", - "rhododendron 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "substrate-consensus-rhd 0.1.0", - "substrate-network 0.1.0", - "substrate-primitives 0.1.0", - "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "node-primitives" version = "0.1.0" @@ -1682,8 +1650,8 @@ dependencies = [ "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 0.1.0", "sr-std 0.1.0", "substrate-primitives 0.1.0", @@ -1700,8 +1668,8 @@ dependencies = [ "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-api 0.1.0", "sr-io 0.1.0", "sr-primitives 0.1.0", @@ -1719,6 +1687,7 @@ dependencies = [ "srml-system 0.1.0", "srml-timestamp 0.1.0", "srml-treasury 0.1.0", + "srml-upgrade-key 0.1.0", "substrate-keyring 0.1.0", "substrate-primitives 0.1.0", ] @@ -1793,12 +1762,12 @@ dependencies = [ "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.37 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "openssl" -version = "0.10.13" +version = "0.10.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1806,12 +1775,12 @@ dependencies = [ "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.37 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "openssl-sys" -version = "0.9.37" +version = "0.9.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1833,21 +1802,13 @@ name = "parity-bytes" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "parity-codec" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "parity-codec" version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2200,20 +2161,10 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rhododendron" -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.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "rhododendron" version = "0.4.0" -source = "git+https://github.com/paritytech/rhododendron.git#64b46b577479a3b6c493fa6db5420a265a445ff9" +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.25 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2356,20 +2307,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "serde_derive" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.9 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2379,7 +2327,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2467,7 +2415,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2528,8 +2476,8 @@ dependencies = [ "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-std 0.1.0", @@ -2562,8 +2510,8 @@ version = "0.1.0" dependencies = [ "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 0.1.0", "sr-std 0.1.0", ] @@ -2575,8 +2523,8 @@ dependencies = [ "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-primitives 0.1.0", "sr-std 0.1.0", @@ -2593,8 +2541,8 @@ dependencies = [ "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-primitives 0.1.0", "sr-std 0.1.0", @@ -2611,8 +2559,8 @@ dependencies = [ "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-primitives 0.1.0", "sr-std 0.1.0", @@ -2630,8 +2578,8 @@ dependencies = [ "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", "pwasm-utils 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-primitives 0.1.0", "sr-sandbox 0.1.0", @@ -2651,8 +2599,8 @@ dependencies = [ "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-primitives 0.1.0", "sr-std 0.1.0", @@ -2671,8 +2619,8 @@ dependencies = [ "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-primitives 0.1.0", "sr-std 0.1.0", @@ -2689,8 +2637,8 @@ dependencies = [ "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-primitives 0.1.0", "sr-std 0.1.0", @@ -2707,8 +2655,8 @@ dependencies = [ "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-primitives 0.1.0", "sr-std 0.1.0", @@ -2726,8 +2674,8 @@ dependencies = [ "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-primitives 0.1.0", "sr-std 0.1.0", @@ -2746,8 +2694,8 @@ dependencies = [ "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-primitives 0.1.0", "sr-std 0.1.0", @@ -2770,8 +2718,8 @@ dependencies = [ "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-primitives 0.1.0", "sr-std 0.1.0", @@ -2786,8 +2734,8 @@ dependencies = [ "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-primitives 0.1.0", "sr-std 0.1.0", @@ -2802,8 +2750,8 @@ dependencies = [ "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-primitives 0.1.0", "sr-std 0.1.0", @@ -2820,8 +2768,8 @@ dependencies = [ "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-primitives 0.1.0", "sr-std 0.1.0", @@ -2831,6 +2779,24 @@ dependencies = [ "substrate-primitives 0.1.0", ] +[[package]] +name = "srml-upgrade-key" +version = "0.1.0" +dependencies = [ + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 0.1.0", + "sr-primitives 0.1.0", + "sr-std 0.1.0", + "srml-consensus 0.1.0", + "srml-support 0.1.0", + "srml-system 0.1.0", + "substrate-primitives 0.1.0", +] + [[package]] name = "stable_deref_trait" version = "1.1.1" @@ -2876,7 +2842,7 @@ dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "node-cli 0.1.0", - "vergen 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "vergen 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2928,7 +2894,7 @@ dependencies = [ "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "sr-api 0.1.0", "sr-primitives 0.1.0", - "substrate-consensus-rhd 0.1.0", + "substrate-consensus-common 0.1.0", "substrate-executor 0.1.0", "substrate-keyring 0.1.0", "substrate-primitives 0.1.0", @@ -2961,11 +2927,44 @@ dependencies = [ "substrate-trie 0.4.0", ] +[[package]] +name = "substrate-consensus-aura" +version = "0.1.0" +dependencies = [ + "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.25 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 0.1.0", + "sr-primitives 0.1.0", + "sr-version 0.1.0", + "srml-consensus 0.1.0", + "srml-support 0.1.0", + "substrate-client 0.1.0", + "substrate-consensus-common 0.1.0", + "substrate-executor 0.1.0", + "substrate-keyring 0.1.0", + "substrate-network 0.1.0", + "substrate-primitives 0.1.0", + "substrate-service 0.3.0", + "substrate-test-client 0.1.0", + "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "substrate-consensus-common" version = "0.1.0" dependencies = [ + "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-primitives 0.1.0", + "sr-version 0.1.0", "substrate-primitives 0.1.0", + "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2973,21 +2972,25 @@ name = "substrate-consensus-rhd" 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.25 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-codec 1.1.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.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rhododendron 0.4.0 (git+https://github.com/paritytech/rhododendron.git)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "rhododendron 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-primitives 0.1.0", "sr-version 0.1.0", "srml-consensus 0.1.0", "srml-support 0.1.0", + "srml-system 0.1.0", + "substrate-client 0.1.0", + "substrate-consensus-common 0.1.0", "substrate-executor 0.1.0", "substrate-keyring 0.1.0", "substrate-primitives 0.1.0", + "substrate-transaction-pool 0.1.0", "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3003,8 +3006,8 @@ dependencies = [ "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-version 0.1.0", "substrate-primitives 0.1.0", @@ -3049,8 +3052,8 @@ dependencies = [ "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "parity-crypto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-primitives 0.1.0", "subtle 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3063,8 +3066,8 @@ version = "0.1.0" dependencies = [ "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3084,6 +3087,7 @@ dependencies = [ "rustc-hex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 0.1.0", "substrate-client 0.1.0", + "substrate-consensus-common 0.1.0", "substrate-keyring 0.1.0", "substrate-network-libp2p 0.1.0", "substrate-primitives 0.1.0", @@ -3106,8 +3110,8 @@ dependencies = [ "parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3133,8 +3137,8 @@ dependencies = [ "pretty_assertions 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-std 0.1.0", "substrate-serializer 0.1.0", "twox-hash 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3160,6 +3164,7 @@ dependencies = [ "sr-primitives 0.1.0", "sr-version 0.1.0", "substrate-client 0.1.0", + "substrate-consensus-common 0.1.0", "substrate-executor 0.1.0", "substrate-primitives 0.1.0", "substrate-test-client 0.1.0", @@ -3175,7 +3180,7 @@ dependencies = [ "jsonrpc-pubsub 9.0.0 (git+https://github.com/paritytech/jsonrpc.git)", "jsonrpc-ws-server 9.0.0 (git+https://github.com/paritytech/jsonrpc.git)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 0.1.0", "substrate-rpc 0.1.0", ] @@ -3184,7 +3189,7 @@ dependencies = [ name = "substrate-serializer" version = "0.1.0" dependencies = [ - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3199,14 +3204,15 @@ dependencies = [ "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", "slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 0.1.0", "sr-primitives 0.1.0", "substrate-client 0.1.0", "substrate-client-db 0.1.0", + "substrate-consensus-common 0.1.0", "substrate-executor 0.1.0", "substrate-keystore 0.1.0", "substrate-network 0.1.0", @@ -3229,6 +3235,7 @@ dependencies = [ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 0.1.0", "substrate-client 0.1.0", + "substrate-consensus-common 0.1.0", "substrate-network 0.1.0", "substrate-primitives 0.1.0", "substrate-service 0.3.0", @@ -3285,6 +3292,7 @@ dependencies = [ "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 0.1.0", "substrate-client 0.1.0", + "substrate-consensus-common 0.1.0", "substrate-executor 0.1.0", "substrate-keyring 0.1.0", "substrate-primitives 0.1.0", @@ -3299,8 +3307,8 @@ dependencies = [ "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-api 0.1.0", "sr-io 0.1.0", "sr-primitives 0.1.0", @@ -3320,8 +3328,8 @@ dependencies = [ "futures 0.1.25 (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.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 0.1.0", "substrate-test-runtime 0.1.0", ] @@ -3387,7 +3395,7 @@ dependencies = [ [[package]] name = "syn" -version = "0.15.9" +version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3912,7 +3920,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "vergen" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3936,8 +3944,8 @@ name = "wabt" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", "wabt-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4066,7 +4074,7 @@ dependencies = [ "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.14 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4159,7 +4167,7 @@ dependencies = [ "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" "checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" "checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda" -"checksum ctr 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50ac3add446ec1f8fe3dc007cd838f5b22bbf33186394feac505451ecc43c018" +"checksum ctr 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4b669fcb8e20124db86dbd9b01e74ec0e9e420e65381311ce5249864fc7ff0c0" "checksum ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "630391922b1b893692c6334369ff528dcc3a9d8061ccf4c803aa8f83cb13db5e" "checksum datastore 0.1.0 (git+https://github.com/tomaka/libp2p-rs?rev=8111062f0177fd7423626f2db9560273644a4c4d)" = "" "checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8" @@ -4193,7 +4201,7 @@ dependencies = [ "checksum getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0a7292d30132fb5424b354f5dc02512a86e4c516fe544bb7a25e7f266951b797" "checksum getset 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "54c7f36a235738bb25904d6a2b3dbb28f6f5736cd3918c4bf80d6bb236200782" "checksum globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4743617a7464bbda3c8aec8558ff2f9429047e025771037df561d383337ff865" -"checksum h2 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "a27e7ed946e8335bdf9a191bc1b9b14a03ba822d013d2f58437f4fabcbd7fc2c" +"checksum h2 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "7dd33bafe2e6370e6c8eb0cf1b8c5f93390b90acde7e9b03723f166b28b648ed" "checksum hash-db 0.9.0 (git+https://github.com/paritytech/trie)" = "" "checksum hash256-std-hasher 0.9.0 (git+https://github.com/paritytech/trie)" = "" "checksum heapsize 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461" @@ -4204,7 +4212,7 @@ dependencies = [ "checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" "checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e" "checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2" -"checksum hyper 0.12.11 (registry+https://github.com/rust-lang/crates.io-index)" = "78d50abbd1790e0f4c74cb1d4a2211b439bac661d54107ad5564c55e77906762" +"checksum hyper 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)" = "4aca412c241a2dd53af261efc7adf7736fdebd67dc0d1cc1ffdbcb9407e0e810" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum indexmap 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08173ba1e906efb6538785a8844dd496f5d34f0a2d88038e95195172fc667220" "checksum integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)" = "" @@ -4282,12 +4290,11 @@ dependencies = [ "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" "checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" "checksum opaque-debug 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d620c9c26834b34f039489ac0dfdb12c7ac15ccaf818350a64c9b5334a452ad7" -"checksum openssl 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "5af9e83eb3c51ee806387d26a43056f3246d865844caa6dd704d2ba7e831c264" +"checksum openssl 0.10.14 (registry+https://github.com/rust-lang/crates.io-index)" = "6285ab297861546af7a2753593b3160bfc09f0ab9d1f5bb009e39d81a169b499" "checksum openssl 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "a3605c298474a3aa69de92d21139fb5e2a81688d308262359d85cdd0d12a7985" -"checksum openssl-sys 0.9.37 (registry+https://github.com/rust-lang/crates.io-index)" = "d4edbc8dfa63f557aee3a498179af2cc6a989e12ba1751840046c79afc9e615a" +"checksum openssl-sys 0.9.39 (registry+https://github.com/rust-lang/crates.io-index)" = "278c1ad40a89aa1e741a1eed089a2f60b18fab8089c3139b542140fc7d674106" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum parity-bytes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5168b4cf41f3835e4bc6ffb32f51bc9365dc50cb351904595b3931d917fd0c" -"checksum parity-codec 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bee4edfcfa19892f7178cb299a659866015dc131459865a1d808269cf7e7eb9e" "checksum parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "dca389ea5e1632c89b2ce54f7e2b4a8a8c9d278042222a91e0bf95451218cb4c" "checksum parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ffa42c2cb493b60b12c75b26e8c94cb734af4df4d7f2cc229dc04c1953dac189" "checksum parity-crypto 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c1117f6574377d21309bfa1f7d69ff734120685d92b02c3f362b122585758840" @@ -4330,8 +4337,7 @@ dependencies = [ "checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" "checksum regex-syntax 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "747ba3b235651f6e2f67dfa8bcdcd073ddb7c243cb21c442fc12395dfcac212d" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" -"checksum rhododendron 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e20523445e693f394c0e487113ae656071311c9ee4c1e914441bece8c929b21d" -"checksum rhododendron 0.4.0 (git+https://github.com/paritytech/rhododendron.git)" = "" +"checksum rhododendron 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a09bc21b21795c366c8bf0e87afb71175f5f736b3a5b279b6f4e81839d0a877b" "checksum ring 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6f7d28b30a72c01b458428e0ae988d4149c20d902346902be881e3edc4bb325c" "checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" "checksum rustc-demangle 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "bcfe5b13211b4d78e5c2cadfebd7769197d95c639c35a50057eb4c05de811395" @@ -4350,8 +4356,8 @@ dependencies = [ "checksum security-framework-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "5421621e836278a0b139268f36eee0dc7e389b784dc3f79d8f11aabadf41bead" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "84257ccd054dc351472528c8587b4de2dbf0dc0fe2e634030c1a90bfdacebaa9" -"checksum serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "31569d901045afbff7a9479f793177fe9259819aff10ab4f89ef69bbc5f567fe" +"checksum serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "15c141fc7027dd265a47c090bf864cf62b42c4d228bbcf4e51a0c9e2b0d3f7ef" +"checksum serde_derive 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "225de307c6302bec3898c51ca302fc94a7a1697ef0845fcee6448f33c032249c" "checksum serde_json 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "43344e7ce05d0d8280c5940cabb4964bea626aa58b1ec0e8c73fa2a8512a38ce" "checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c" "checksum sha1 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "171698ce4ec7cbb93babeb3190021b4d72e96ccb98e33d277ae4ea959d6f2d9e" @@ -4376,7 +4382,7 @@ dependencies = [ "checksum subtle 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc7f6353c2ee5407358d063a14cccc1630804527090a6fb5a9489ce4924280fb" "checksum syn 0.13.11 (registry+https://github.com/rust-lang/crates.io-index)" = "14f9bf6292f3a61d2c716723fdb789a41bbe104168e6f496dc6497e531ea1b9b" "checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" -"checksum syn 0.15.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b10ee269228fb723234fce98e9aac0eaed2bd5f1ad2f6930e8d5b93f04445a1a" +"checksum syn 0.15.12 (registry+https://github.com/rust-lang/crates.io-index)" = "34ab9797e47d24cb76b8dc4d24ff36807018c7cc549c4cba050b068be0c586b0" "checksum synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb9b7550d063ea184027c9b8c20ac167cd36d3e06b3a40bceb9d746dc1a7b7" "checksum sysinfo 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "11c5f6e8a7a7146f26ffed9a5ff8bab2706f1ac8a413a415e1d211b819d5c24d" "checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" @@ -4433,7 +4439,7 @@ dependencies = [ "checksum utf8-ranges 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd70f467df6810094968e2fce0ee1bd0e87157aceb026a8c083bcf5e25b9efe4" "checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" -"checksum vergen 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4cae5a72131fdf47d4fbc9286393ec8622ec7a5502fbe77b291d9aba21d3f179" +"checksum vergen 2.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "93fb2d57fbc535fcd45548c99b141d2d960995daaf04b864c4d9fe1ea011c819" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum wabt 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "182ae543249ccf2705f324d233891c1176fca142e137b55ba43d9dbfe93f18a2" diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index f47fa42507..54ae7d9d82 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -23,6 +23,7 @@ members = [ "core/client", "core/client/db", "core/consensus/common", + "core/consensus/aura", "core/consensus/rhd", "core/executor", "core/finality-grandpa", @@ -54,6 +55,7 @@ members = [ "srml/system", "srml/timestamp", "srml/treasury", + "srml/upgrade-key", "core/serializer", "core/service", "core/service/test", @@ -64,9 +66,7 @@ members = [ "core/trie", "core/keystore", "node/cli", - "node/consensus", "node/executor", - "node/network", "node/primitives", "node/runtime", "subkey", diff --git a/substrate/core/client/Cargo.toml b/substrate/core/client/Cargo.toml index ba457fbb9c..5eeb75940d 100644 --- a/substrate/core/client/Cargo.toml +++ b/substrate/core/client/Cargo.toml @@ -12,7 +12,7 @@ hex-literal = "0.1" futures = "0.1.17" slog = "^2" heapsize = "0.4" -substrate-consensus-rhd = { path = "../consensus/rhd" } +substrate-consensus-common = { path = "../consensus/common" } parity-codec = "2.1" substrate-executor = { path = "../executor" } substrate-primitives = { path = "../primitives" } diff --git a/substrate/core/client/db/src/lib.rs b/substrate/core/client/db/src/lib.rs index f35dd8a05d..36ab5f4b2a 100644 --- a/substrate/core/client/db/src/lib.rs +++ b/substrate/core/client/db/src/lib.rs @@ -534,7 +534,9 @@ impl Backend { meta.finalized_hash, f_hash), ).into()) } - transaction.put(columns::META, meta_keys::FINALIZED_BLOCK, f_hash.as_ref()); + + let lookup_key = ::utils::number_to_lookup_key(f_num); + transaction.put(columns::META, meta_keys::FINALIZED_BLOCK, &lookup_key); let commit = self.storage.state_db.canonicalize_block(&f_hash); apply_state_commit(transaction, commit); @@ -586,11 +588,20 @@ impl client::backend::Backend for Backend whe -> Result<(), client::error::Error> { let mut transaction = DBTransaction::new(); + if let Some(pending_block) = operation.pending_block { let hash = pending_block.header.hash(); let parent_hash = *pending_block.header.parent_hash(); let number = pending_block.header.number().clone(); + // blocks in longest chain are keyed by number + let lookup_key = if pending_block.leaf_state.is_best() { + ::utils::number_to_lookup_key(number).to_vec() + } else { + // other blocks are keyed by number + hash + ::utils::number_and_hash_to_lookup_key(number, hash) + }; + if pending_block.leaf_state.is_best() { let meta = self.blockchain.meta.read(); @@ -678,17 +689,9 @@ impl client::backend::Backend for Backend whe } } - transaction.put(columns::META, meta_keys::BEST_BLOCK, hash.as_ref()); + transaction.put(columns::META, meta_keys::BEST_BLOCK, &lookup_key); } - // blocks in longest chain are keyed by number - let lookup_key = if pending_block.leaf_state.is_best() { - ::utils::number_to_lookup_key(number).to_vec() - } else { - // other blocks are keyed by number + hash - ::utils::number_and_hash_to_lookup_key(number, hash) - }; - transaction.put(columns::HEADER, &lookup_key, &pending_block.header.encode()); if let Some(body) = pending_block.body { transaction.put(columns::BODY, &lookup_key, &body.encode()); @@ -700,7 +703,7 @@ impl client::backend::Backend for Backend whe transaction.put(columns::HASH_LOOKUP, hash.as_ref(), &lookup_key); if number == Zero::zero() { - transaction.put(columns::META, meta_keys::FINALIZED_BLOCK, hash.as_ref()); + transaction.put(columns::META, meta_keys::FINALIZED_BLOCK, &lookup_key); transaction.put(columns::META, meta_keys::GENESIS_HASH, hash.as_ref()); } @@ -797,7 +800,8 @@ impl client::backend::Backend for Backend whe || client::error::ErrorKind::UnknownBlock( format!("Error reverting to {}. Block header not found.", best)))?; - transaction.put(columns::META, meta_keys::BEST_BLOCK, header.hash().as_ref()); + let lookup_key = ::utils::number_to_lookup_key(header.number().clone()); + transaction.put(columns::META, meta_keys::BEST_BLOCK, &lookup_key); transaction.delete(columns::HASH_LOOKUP, header.hash().as_ref()); self.storage.db.write(transaction).map_err(db_err)?; self.blockchain.update_meta(header.hash().clone(), best.clone(), true, false); @@ -927,40 +931,49 @@ mod tests { #[test] fn block_hash_inserted_correctly() { - let db = Backend::::new_test(1, 0); - for i in 0..10 { - assert!(db.blockchain().hash(i).unwrap().is_none()); + let backing = { + let db = Backend::::new_test(1, 0); + for i in 0..10 { + assert!(db.blockchain().hash(i).unwrap().is_none()); - { - let id = if i == 0 { - BlockId::Hash(Default::default()) - } else { - BlockId::Number(i - 1) - }; - - let mut op = db.begin_operation(id).unwrap(); - let header = Header { - number: i, - parent_hash: if i == 0 { - Default::default() + { + let id = if i == 0 { + BlockId::Hash(Default::default()) } else { - db.blockchain.hash(i - 1).unwrap().unwrap() - }, - state_root: Default::default(), - digest: Default::default(), - extrinsics_root: Default::default(), - }; + BlockId::Number(i - 1) + }; - op.set_block_data( - header, - Some(vec![]), - None, - NewBlockState::Best, - ).unwrap(); - db.commit_operation(op).unwrap(); + let mut op = db.begin_operation(id).unwrap(); + let header = Header { + number: i, + parent_hash: if i == 0 { + Default::default() + } else { + db.blockchain.hash(i - 1).unwrap().unwrap() + }, + state_root: Default::default(), + digest: Default::default(), + extrinsics_root: Default::default(), + }; + + op.set_block_data( + header, + Some(vec![]), + None, + NewBlockState::Best, + ).unwrap(); + db.commit_operation(op).unwrap(); + } + + assert!(db.blockchain().hash(i).unwrap().is_some()) } + db.storage.db.clone() + }; - assert!(db.blockchain().hash(i).unwrap().is_some()) + let backend = Backend::::from_kvdb(backing, PruningMode::keep_blocks(1), 0).unwrap(); + assert_eq!(backend.blockchain().info().unwrap().best_number, 9); + for i in 0..10 { + assert!(backend.blockchain().hash(i).unwrap().is_some()) } } diff --git a/substrate/core/client/db/src/light.rs b/substrate/core/client/db/src/light.rs index 49e5e85be1..f336df4d28 100644 --- a/substrate/core/client/db/src/light.rs +++ b/substrate/core/client/db/src/light.rs @@ -196,7 +196,8 @@ impl LightStorage { ).into()) } - transaction.put(columns::META, meta_keys::FINALIZED_BLOCK, hash.as_ref()); + let lookup_key = ::utils::number_to_lookup_key(header.number().clone()); + transaction.put(columns::META, meta_keys::FINALIZED_BLOCK, &lookup_key); // build new CHT if required if let Some(new_cht_number) = cht::is_build_required(cht::SIZE, *header.number()) { @@ -244,6 +245,14 @@ impl LightBlockchainStorage for LightStorage let number = *header.number(); let parent_hash = *header.parent_hash(); + // blocks in longest chain are keyed by number + let lookup_key = if leaf_state.is_best() { + ::utils::number_to_lookup_key(number).to_vec() + } else { + // other blocks are keyed by number + hash + ::utils::number_and_hash_to_lookup_key(number, hash) + }; + if leaf_state.is_best() { // handle reorg. { @@ -298,17 +307,9 @@ impl LightBlockchainStorage for LightStorage } } - transaction.put(columns::META, meta_keys::BEST_BLOCK, hash.as_ref()); + transaction.put(columns::META, meta_keys::BEST_BLOCK, &lookup_key); } - // blocks in longest chain are keyed by number - let lookup_key = if leaf_state.is_best() { - ::utils::number_to_lookup_key(number).to_vec() - } else { - // other blocks are keyed by number + hash - ::utils::number_and_hash_to_lookup_key(number, hash) - }; - transaction.put(columns::HEADER, &lookup_key, &header.encode()); transaction.put(columns::HASH_LOOKUP, hash.as_ref(), &lookup_key); diff --git a/substrate/core/client/db/src/utils.rs b/substrate/core/client/db/src/utils.rs index b32b56bccc..f7b81845e8 100644 --- a/substrate/core/client/db/src/utils.rs +++ b/substrate/core/client/db/src/utils.rs @@ -53,6 +53,7 @@ pub mod meta_keys { } /// Database metadata. +#[derive(Debug)] pub struct Meta { /// Hash of the best known block. pub best_hash: H, diff --git a/substrate/core/client/src/call_executor.rs b/substrate/core/client/src/call_executor.rs index 2b442a3569..168d0a26c5 100644 --- a/substrate/core/client/src/call_executor.rs +++ b/substrate/core/client/src/call_executor.rs @@ -150,7 +150,7 @@ where let heap_pages = state.storage(well_known_keys::HEAP_PAGES) .map_err(|e| error::ErrorKind::Execution(Box::new(e)))? .and_then(|v| u64::decode(&mut &v[..])) - .unwrap_or(8) as usize; + .unwrap_or(1024) as usize; let mut ext = Ext::new(&mut overlay, &state, self.backend.changes_trie_storage()); self.executor.runtime_version(&mut ext, heap_pages, &code) diff --git a/substrate/core/client/src/client.rs b/substrate/core/client/src/client.rs index fcf917584e..8ef2d7e1eb 100644 --- a/substrate/core/client/src/client.rs +++ b/substrate/core/client/src/client.rs @@ -26,6 +26,7 @@ use runtime_primitives::{ generic::{BlockId, SignedBlock, Block as RuntimeBlock}, transaction_validity::{TransactionValidity, TransactionTag}, }; +use consensus::{ImportBlock, ImportResult, BlockOrigin}; use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, As, NumberFor, CurrentHeight, BlockNumberToHash}; use runtime_primitives::{ApplyResult, BuildStorage}; use runtime_api as api; @@ -44,7 +45,7 @@ use blockchain::{self, Info as ChainInfo, Backend as ChainBackend, HeaderBackend use call_executor::{CallExecutor, LocalCallExecutor}; use executor::{RuntimeVersion, RuntimeInfo}; use notifications::{StorageNotifications, StorageEventStream}; -use {cht, error, in_mem, block_builder, genesis}; +use {cht, error, in_mem, block_builder, genesis, consensus}; /// Type that implements `futures::Stream` of block import events. pub type ImportNotifications = mpsc::UnboundedReceiver>; @@ -106,21 +107,6 @@ pub struct ClientInfo { pub best_queued_hash: Option, } -/// Block import result. -#[derive(Debug)] -pub enum ImportResult { - /// Added to the import queue. - Queued, - /// Already in the import queue. - AlreadyQueued, - /// Already in the blockchain. - AlreadyInChain, - /// Block or parent is known to be bad. - KnownBad, - /// Block parent is not in the chain. - UnknownParent, -} - /// Block status. #[derive(Debug, PartialEq, Eq)] pub enum BlockStatus { @@ -134,70 +120,6 @@ pub enum BlockStatus { Unknown, } -/// Block data origin. -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum BlockOrigin { - /// Genesis block built into the client. - Genesis, - /// Block is part of the initial sync with the network. - NetworkInitialSync, - /// Block was broadcasted on the network. - NetworkBroadcast, - /// Block that was received from the network and validated in the consensus process. - ConsensusBroadcast, - /// Block that was collated by this node. - Own, - /// Block was imported from a file. - File, -} - -/// Data required to import a Block -pub struct ImportBlock { - /// Origin of the Block - pub origin: BlockOrigin, - /// Header - pub header: Block::Header, - /// Justification provided for this block from the outside - pub external_justification: Justification, - /// Internal Justification for the block - pub internal_justification: Vec, // Block::Digest::DigestItem? - /// Block's body - pub body: Option>, - /// Is this block finalized already? - /// `true` implies instant finality. - pub finalized: bool, - /// Auxiliary consensus data produced by the block. - /// Contains a list of key-value pairs. If values are `None`, the keys - /// will be deleted. - pub auxiliary: Vec<(Vec, Option>)>, -} - -impl ImportBlock { - /// Deconstruct the justified header into parts. - pub fn into_inner(self) - -> ( - BlockOrigin, - ::Header, - Justification, - Justification, - Option::Extrinsic>>, - bool, - Vec<(Vec, Option>)>, - ) { - ( - self.origin, - self.header, - self.external_justification, - self.internal_justification, - self.body, - self.finalized, - self.auxiliary, - ) - } -} - - - /// Summary of an imported block #[derive(Clone, Debug)] pub struct BlockImportNotification { @@ -222,6 +144,41 @@ pub struct FinalityNotification { pub header: Block::Header, } +// used in importing a block, where additional changes are made after the runtime +// executed. +enum PrePostHeader { + // they are the same: no post-runtime digest items. + Same(H), + // different headers (pre, post). + Different(H, H), +} + +impl PrePostHeader { + // get a reference to the "pre-header" -- the header as it should be just after the runtime. + fn pre(&self) -> &H { + match *self { + PrePostHeader::Same(ref h) => h, + PrePostHeader::Different(ref h, _) => h, + } + } + + // get a reference to the "post-header" -- the header as it should be after all changes are applied. + fn post(&self) -> &H { + match *self { + PrePostHeader::Same(ref h) => h, + PrePostHeader::Different(_, ref h) => h, + } + } + + // convert to the "post-header" -- the header as it should be after all changes are applied. + fn into_post(self) -> H { + match self { + PrePostHeader::Same(h) => h, + PrePostHeader::Different(_, h) => h, + } + } +} + /// Create an instance of in-memory client. pub fn new_in_mem( executor: E, @@ -513,52 +470,6 @@ impl Client where ) } - /// Import a checked and validated block - pub fn import_block( - &self, - import_block: ImportBlock, - new_authorities: Option>, - ) -> error::Result { - - let ( - origin, - header, - _, - justification, - body, - finalized, - _aux, // TODO: write this to DB also - ) = import_block.into_inner(); - let parent_hash = header.parent_hash().clone(); - - match self.backend.blockchain().status(BlockId::Hash(parent_hash))? { - blockchain::BlockStatus::InChain => {}, - blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent), - } - let hash = header.hash(); - let _import_lock = self.import_lock.lock(); - let height: u64 = header.number().as_(); - *self.importing_block.write() = Some(hash); - - let result = self.execute_and_import_block( - origin, - hash, - header, - justification, - body, - new_authorities, - finalized, - ); - - *self.importing_block.write() = None; - telemetry!("block.import"; - "height" => height, - "best" => ?hash, - "origin" => ?origin - ); - result - } - // TODO [ToDr] Optimize and re-use tags from the pool. fn transaction_tags(&self, at: Block::Hash, body: &Option>) -> error::Result> { let id = BlockId::Hash(at); @@ -587,13 +498,13 @@ impl Client where &self, origin: BlockOrigin, hash: Block::Hash, - header: Block::Header, + import_headers: PrePostHeader, justification: Justification, body: Option>, authorities: Option>, finalized: bool, ) -> error::Result { - let parent_hash = header.parent_hash().clone(); + let parent_hash = import_headers.post().parent_hash().clone(); match self.backend.blockchain().status(BlockId::Hash(hash))? { blockchain::BlockStatus::InChain => return Ok(ImportResult::AlreadyInChain), blockchain::BlockStatus::Unknown => {}, @@ -627,12 +538,13 @@ impl Client where transaction_state, &mut overlay, "execute_block", - &::new(header.clone(), body.clone().unwrap_or_default()).encode(), + &::new(import_headers.pre().clone(), body.clone().unwrap_or_default()).encode(), match (origin, self.block_execution_strategy) { (BlockOrigin::NetworkInitialSync, _) | (_, ExecutionStrategy::NativeWhenPossible) => ExecutionManager::NativeWhenPossible, (_, ExecutionStrategy::AlwaysWasm) => ExecutionManager::AlwaysWasm, _ => ExecutionManager::Both(|wasm_result, native_result| { + let header = import_headers.post(); warn!("Consensus error between wasm and native block execution at block {}", hash); warn!(" Header {:?}", header); warn!(" Native result {:?}", native_result); @@ -654,7 +566,7 @@ impl Client where }; // TODO: non longest-chain rule. - let is_new_best = finalized || header.number() > &last_best_number; + let is_new_best = finalized || import_headers.post().number() > &last_best_number; let leaf_state = if finalized { ::backend::NewBlockState::Final } else if is_new_best { @@ -663,10 +575,10 @@ impl Client where ::backend::NewBlockState::Normal }; - trace!("Imported {}, (#{}), best={}, origin={:?}", hash, header.number(), is_new_best, origin); + trace!("Imported {}, (#{}), best={}, origin={:?}", hash, import_headers.post().number(), is_new_best, origin); transaction.set_block_data( - header.clone(), + import_headers.post().clone(), body, Some(justification), leaf_state, @@ -693,7 +605,7 @@ impl Client where if finalized { let notification = FinalityNotification:: { hash, - header: header.clone(), + header: import_headers.post().clone(), }; self.finality_notification_sinks.lock() @@ -703,7 +615,7 @@ impl Client where let notification = BlockImportNotification:: { hash, origin, - header, + header: import_headers.into_post(), is_new_best, tags, }; @@ -979,6 +891,84 @@ impl Client where } } + +impl consensus::BlockImport for Client where + B: backend::Backend, + E: CallExecutor + Clone, + Block: BlockT, +{ + type Error = Error; + + /// Import a checked and validated block + fn import_block( + &self, + import_block: ImportBlock, + new_authorities: Option>, + ) -> Result { + use runtime_primitives::traits::Digest; + + let ImportBlock { + origin, + header, + external_justification, + post_runtime_digests, + body, + finalized, + .. + } = import_block; + let parent_hash = header.parent_hash().clone(); + + match self.backend.blockchain().status(BlockId::Hash(parent_hash))? { + blockchain::BlockStatus::InChain => {}, + blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent), + } + + let import_headers = if post_runtime_digests.is_empty() { + PrePostHeader::Same(header) + } else { + let mut post_header = header.clone(); + for item in post_runtime_digests { + post_header.digest_mut().push(item); + } + PrePostHeader::Different(header, post_header) + }; + + let hash = import_headers.post().hash(); + let _import_lock = self.import_lock.lock(); + let height: u64 = import_headers.post().number().as_(); + *self.importing_block.write() = Some(hash); + + let result = self.execute_and_import_block( + origin, + hash, + import_headers, + external_justification, + body, + new_authorities, + finalized, + ); + + *self.importing_block.write() = None; + telemetry!("block.import"; + "height" => height, + "best" => ?hash, + "origin" => ?origin + ); + result.map_err(|e| e.into()) + } +} + +impl consensus::Authorities for Client where + B: backend::Backend, + E: CallExecutor + Clone, + Block: BlockT, +{ + type Error = Error; + fn authorities(&self, at: &BlockId) -> Result, Self::Error> { + self.authorities_at(at).map_err(|e| e.into()) + } +} + impl CurrentHeight for Client where B: backend::Backend, E: CallExecutor + Clone, @@ -1135,26 +1125,6 @@ impl api::BlockBuilder for Client where } } -impl api::OldTxQueue for Client where - B: backend::Backend, - E: CallExecutor, - Block: BlockT, -{ - type Error = Error; - - fn account_nonce( - &self, at: &BlockId, account: &AccountId - ) -> Result { - self.call_api_at(at, "account_nonce", &(account)) - } - - fn lookup_address( - &self, at: &BlockId, address: &Address - ) -> Result, Self::Error> { - self.call_api_at(at, "lookup_address", &(address)) - } -} - impl api::TaggedTransactionQueue for Client where B: backend::Backend, E: CallExecutor, @@ -1169,30 +1139,6 @@ impl api::TaggedTransactionQueue for Client whe } } -impl api::Miscellaneous for Client where - B: backend::Backend, - E: CallExecutor, - Block: BlockT, -{ - type Error = Error; - - fn validator_count(&self, at: &BlockId) -> Result { - self.call_api_at(at, "validator_count", &()) - } - - fn validators( - &self, at: &BlockId - ) -> Result, Self::Error> { - self.call_api_at(at, "validators", &()) - } - - fn timestamp( - &self, at: &BlockId - ) -> Result { - self.call_api_at(at, "timestamp", &()) - } -} - #[cfg(test)] pub(crate) mod tests { use std::collections::HashMap; @@ -1202,7 +1148,7 @@ pub(crate) mod tests { use runtime_primitives::traits::{Digest as DigestT, DigestItem as DigestItemT}; use runtime_primitives::generic::DigestItem; use test_client::{self, TestClient}; - use test_client::client::BlockOrigin; + use consensus::BlockOrigin; use test_client::client::backend::Backend as TestBackend; use test_client::BlockBuilderExt; use test_client::runtime::{self, Block, Transfer}; diff --git a/substrate/core/client/src/error.rs b/substrate/core/client/src/error.rs index 5abc2617b2..139cef13da 100644 --- a/substrate/core/client/src/error.rs +++ b/substrate/core/client/src/error.rs @@ -16,11 +16,17 @@ //! Substrate client possible errors. +#![allow(missing_docs)] + use std; use state_machine; use runtime_primitives::ApplyError; +use consensus; error_chain! { + links { + Consensus(consensus::Error, consensus::ErrorKind); + } errors { /// Backend error. Backend(s: String) { diff --git a/substrate/core/client/src/lib.rs b/substrate/core/client/src/lib.rs index 43a4be7a80..a033277905 100644 --- a/substrate/core/client/src/lib.rs +++ b/substrate/core/client/src/lib.rs @@ -26,6 +26,7 @@ extern crate parity_codec as codec; extern crate substrate_primitives as primitives; extern crate sr_primitives as runtime_primitives; extern crate substrate_state_machine as state_machine; +extern crate substrate_consensus_common as consensus; #[cfg(test)] extern crate substrate_keyring as keyring; #[cfg(test)] extern crate substrate_test_client as test_client; #[macro_use] extern crate substrate_telemetry; @@ -63,8 +64,8 @@ pub use call_executor::{CallResult, CallExecutor, LocalCallExecutor}; pub use client::{ new_with_backend, new_in_mem, - BlockBody, BlockStatus, BlockOrigin, ImportNotifications, FinalityNotifications, BlockchainEvents, - Client, ClientInfo, ChainHead, ImportResult, ImportBlock, + BlockBody, BlockStatus, ImportNotifications, FinalityNotifications, BlockchainEvents, + Client, ClientInfo, ChainHead, }; pub use notifications::{StorageEventStream, StorageChangeSet}; pub use state_machine::ExecutionStrategy; diff --git a/substrate/core/client/src/light/fetcher.rs b/substrate/core/client/src/light/fetcher.rs index 702c9b7034..38a46c7d68 100644 --- a/substrate/core/client/src/light/fetcher.rs +++ b/substrate/core/client/src/light/fetcher.rs @@ -270,7 +270,8 @@ pub mod tests { use error::Error as ClientError; use test_client::{self, TestClient}; use test_client::runtime::{self, Hash, Block, Header}; - use test_client::client::BlockOrigin; + use consensus::BlockOrigin; + use in_mem::{Blockchain as InMemoryBlockchain}; use light::fetcher::{Fetcher, FetchChecker, LightDataChecker, RemoteCallRequest, RemoteHeaderRequest}; diff --git a/substrate/core/consensus/aura/Cargo.toml b/substrate/core/consensus/aura/Cargo.toml new file mode 100644 index 0000000000..0f403ab2ff --- /dev/null +++ b/substrate/core/consensus/aura/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "substrate-consensus-aura" +version = "0.1.0" +authors = ["Parity Technologies "] +description = "Rhododendron Round-Based consensus-algorithm for substrate" + +[dependencies] +futures = "0.1.17" +parity-codec = { version = "2.1" } +substrate-consensus-common = { path = "../common" } +substrate-client = { path = "../../client" } +substrate-primitives = { path = "../../primitives" } +substrate-network = { path = "../../network" } +srml-support = { path = "../../../srml/support" } +sr-primitives = { path = "../../sr-primitives" } +sr-version = { path = "../../sr-version" } +sr-io = { path = "../../sr-io" } +srml-consensus = { path = "../../../srml/consensus" } +tokio = "0.1.7" +parking_lot = "0.4" +error-chain = "0.12" +log = "0.3" + +[dev-dependencies] +substrate-keyring = { path = "../../keyring" } +substrate-executor = { path = "../../executor" } +substrate-service = { path = "../../service" } +substrate-test-client = { path = "../../test-client" } +env_logger = { version = "0.4" } + +[target.'cfg(test)'.dependencies] +substrate-network = { path = "../../network", features = ["test-helpers"] } + +[features] +default = ["std"] +std = [ + "substrate-primitives/std", + "srml-support/std", + "sr-primitives/std", + "sr-version/std", +] + diff --git a/substrate/core/consensus/aura/src/lib.rs b/substrate/core/consensus/aura/src/lib.rs new file mode 100644 index 0000000000..7355119074 --- /dev/null +++ b/substrate/core/consensus/aura/src/lib.rs @@ -0,0 +1,560 @@ +// 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 . + +//! Aura (Authority-round) consensus in substrate. +//! +//! Aura works by having a list of authorities A who are expected to roughly +//! agree on the current time. Time is divided up into discrete slots of t +//! seconds each. For each slot s, the author of that slot is A[s % |A|]. +//! +//! The author is allowed to issue one block but not more during that slot, +//! and it will be built upon the longest valid chain that has been seen. +//! +//! Blocks from future steps will be either deferred or rejected depending on how +//! far in the future they are. + +extern crate parity_codec as codec; +extern crate substrate_consensus_common as consensus_common; +extern crate substrate_client as client; +extern crate substrate_primitives as primitives; +extern crate substrate_network as network; +extern crate srml_support as runtime_support; +extern crate sr_primitives as runtime_primitives; +extern crate sr_version as runtime_version; +extern crate sr_io as runtime_io; +extern crate tokio; + +#[cfg(test)] +extern crate substrate_keyring as keyring; +#[cfg(test)] +extern crate substrate_service as service; +#[cfg(test)] +extern crate substrate_test_client as test_client; +#[cfg(test)] +extern crate env_logger; + +extern crate parking_lot; + +#[macro_use] +extern crate log; + +extern crate futures; + +use std::sync::Arc; +use std::time::{Duration, Instant}; + +use codec::Encode; +use consensus_common::{Authorities, BlockImport, Environment, Proposer}; +use client::ChainHead; +use consensus_common::{ImportBlock, BlockOrigin}; +use runtime_primitives::{generic, generic::BlockId}; +use runtime_primitives::traits::{Block, Header, Digest, DigestItemFor}; +use network::import_queue::{Verifier, BasicQueue}; +use primitives::{AuthorityId, ed25519}; + +use futures::{Stream, Future, IntoFuture, future::{self, Either}}; +use tokio::timer::Interval; + +pub use consensus_common::SyncOracle; + +/// 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=()>; + + /// Send a message at a specific round out. + fn send_message(&self, slot: u64, message: Vec); +} + +/// Configuration for Aura consensus. +#[derive(Clone)] +pub struct Config { + /// The local authority keypair. Can be none if this is just an observer. + pub local_key: Option>, + /// The slot duration in seconds. + pub slot_duration: u64 +} + +/// Get slot author for given block along with authorities. +fn slot_author(slot_num: u64, authorities: &[AuthorityId]) -> Option { + if authorities.is_empty() { return None } + + let idx = slot_num % (authorities.len() as u64); + assert!(idx <= usize::max_value() as u64, + "It is impossible to have a vector with length beyond the address space; qed"); + + let current_author = *authorities.get(idx as usize) + .expect("authorities not empty; index constrained to list length;\ + this is a valid index; qed"); + + Some(current_author) +} + +fn duration_now() -> Option { + use std::time::SystemTime; + + let now = SystemTime::now(); + now.duration_since(SystemTime::UNIX_EPOCH).map_err(|e| { + warn!("Current time {:?} is before unix epoch. Something is wrong: {:?}", now, e); + }).ok() +} + +/// Get the slot for now. +fn slot_now(slot_duration: u64) -> Option { + duration_now().map(|s| s.as_secs() / slot_duration) +} + +/// A digest item which is usable with aura consensus. +pub trait CompatibleDigestItem: Sized { + /// Construct a digest item which is a slot number and a signature on the + /// hash. + fn aura_seal(slot_number: u64, signature: ed25519::Signature) -> Self; + + /// If this item is an Aura seal, return the slot number and signature. + fn as_aura_seal(&self) -> Option<(u64, &ed25519::Signature)>; +} + +impl CompatibleDigestItem for generic::DigestItem { + /// Construct a digest item which is a slot number and a signature on the + /// hash. + fn aura_seal(slot_number: u64, signature: ed25519::Signature) -> Self { + generic::DigestItem::Seal(slot_number, signature) + } + /// If this item is an Aura seal, return the slot number and signature. + fn as_aura_seal(&self) -> Option<(u64, &ed25519::Signature)> { + match self { + generic::DigestItem::Seal(slot, ref sign) => Some((*slot, sign)), + _ => None + } + } +} + +impl CompatibleDigestItem for generic::DigestItem { + /// Construct a digest item which is a slot number and a signature on the + /// hash. + fn aura_seal(slot_number: u64, signature: ed25519::Signature) -> Self { + generic::DigestItem::Seal(slot_number, signature) + } + /// If this item is an Aura seal, return the slot number and signature. + fn as_aura_seal(&self) -> Option<(u64, &ed25519::Signature)> { + match self { + generic::DigestItem::Seal(slot, ref sign) => Some((*slot, sign)), + _ => None + } + } +} + +/// Start the aura worker. This should be run in a tokio runtime. +pub fn start_aura( + config: Config, + client: Arc, + env: Arc, + sync_oracle: SO, +) + -> impl Future where + B: Block, + C: Authorities + BlockImport + ChainHead, + E: Environment, + E::Proposer: Proposer, + SO: SyncOracle + Send + Clone, + DigestItemFor: CompatibleDigestItem, + Error: ::std::error::Error + Send + 'static + From<::consensus_common::Error>, +{ + let make_authorship = move || { + let config = config.clone(); + let client = client.clone(); + let env = env.clone(); + let sync_oracle = sync_oracle.clone(); + + let local_keys = config.local_key.map(|pair| (pair.public(), pair)); + let slot_duration = config.slot_duration; + let mut last_authored_slot = 0; + let next_slot_start = duration_now().map(|now| { + let remaining_full_secs = slot_duration - (now.as_secs() % slot_duration) - 1; + let remaining_nanos = 1_000_000_000 - now.subsec_nanos(); + Instant::now() + Duration::new(remaining_full_secs, remaining_nanos) + }).unwrap_or_else(|| Instant::now()); + + Interval::new(next_slot_start, Duration::from_secs(slot_duration)) + .filter(move |_| !sync_oracle.is_major_syncing()) // only propose when we are not syncing. + .filter_map(move |_| local_keys.clone()) // skip if not authoring. + .map_err(|e| debug!(target: "aura", "Faulty timer: {:?}", e)) + .for_each(move |(public_key, key)| { + use futures::future; + + let slot_num = match slot_now(slot_duration) { + Some(n) => n, + None => return Either::B(future::err(())), + }; + + if last_authored_slot >= slot_num { return Either::B(future::ok(())) } + last_authored_slot = slot_num; + + let chain_head = match client.best_block_header() { + Ok(x) => x, + Err(e) => { + warn!(target:"aura", "Unable to author block in slot {}. no best block header: {:?}", slot_num, e); + return Either::B(future::ok(())) + } + }; + + let authorities = match client.authorities(&BlockId::Hash(chain_head.hash())){ + Ok(authorities) => authorities, + Err(e) => { + warn!("Unable to fetch authorities at block {:?}: {:?}", chain_head.hash(), e); + return Either::B(future::ok(())); + } + }; + + let proposal_work = match slot_author(slot_num, &authorities) { + None => return Either::B(future::ok(())), + Some(author) => if author.0 == public_key.0 { + // we are the slot author. make a block and sign it. + let proposer = match env.init(&chain_head, &authorities, key.clone()) { + Ok(p) => p, + Err(e) => { + warn!("Unable to author block in slot {:?}: {:?}", slot_num, e); + return Either::B(future::ok(())) + } + }; + + proposer.propose().into_future() + } else { + return Either::B(future::ok(())); + } + }; + + let block_import = client.clone(); + Either::A(proposal_work + .map(move |b| { + let (header, body) = b.deconstruct(); + let pre_hash = header.hash(); + let parent_hash = header.parent_hash().clone(); + + // sign the pre-sealed hash of the block and then + // add it to a digest item. + let to_sign = (slot_num, pre_hash).encode(); + let signature = key.sign(&to_sign[..]); + let item = as CompatibleDigestItem>::aura_seal(slot_num, signature); + let import_block = ImportBlock { + origin: BlockOrigin::Own, + header, + external_justification: Vec::new(), + post_runtime_digests: vec![item], + body: Some(body), + finalized: false, + auxiliary: Vec::new(), + }; + + if let Err(e) = block_import.import_block(import_block, None) { + warn!(target: "aura", "Error with block built on {:?}: {:?}", parent_hash, e); + } + }) + .map_err(|e| warn!("Failed to construct block: {:?}", e)) + ) + }) + }; + + future::loop_fn((), move |()| { + let authorship_task = ::std::panic::AssertUnwindSafe(make_authorship()); + authorship_task.catch_unwind().then(|res| { + match res { + Ok(Ok(())) => (), + Ok(Err(())) => warn!("Aura authorship task terminated unexpectedly. Restarting"), + Err(e) => { + if let Some(s) = e.downcast_ref::<&'static str>() { + warn!("Aura authorship task panicked at {:?}", s); + } + + warn!("Restarting Aura authorship task"); + } + } + + Ok(future::Loop::Continue(())) + }) + }) +} + +// a header which has been checked +enum CheckedHeader { + // a header which has slot in the future. this is the full header (not stripped) + // and the slot in which it should be processed. + Deferred(H, u64), + // a header which is fully checked, including signature. This is the pre-header + // accompanied by the seal components. + Checked(H, u64, ed25519::Signature), +} + + +/// check a header has been signed by the right key. If the slot is too far in the future, an error will be returned. +/// if it's successful, returns the pre-header, the slot number, and the signat. +// +// FIXME: needs misbehavior types - https://github.com/paritytech/substrate/issues/1018 +fn check_header(slot_now: u64, mut header: B::Header, hash: B::Hash, authorities: &[AuthorityId]) + -> Result, String> + where DigestItemFor: CompatibleDigestItem +{ + let digest_item = match header.digest_mut().pop() { + Some(x) => x, + None => return Err(format!("Header {:?} is unsealed", hash)), + }; + let (slot_num, &sig) = match digest_item.as_aura_seal() { + Some(x) => x, + None => return Err(format!("Header {:?} is unsealed", hash)), + }; + + if slot_num > slot_now { + header.digest_mut().push(digest_item); + Ok(CheckedHeader::Deferred(header, slot_num)) + } else { + // check the signature is valid under the expected authority and + // chain state. + + let expected_author = match slot_author(slot_num, &authorities) { + None => return Err("Slot Author not found".to_string()), + Some(author) => author + }; + + let pre_hash = header.hash(); + let to_sign = (slot_num, pre_hash).encode(); + let public = ed25519::Public(expected_author.0); + + if ed25519::verify_strong(&sig, &to_sign[..], public) { + Ok(CheckedHeader::Checked(header, slot_num, sig)) + } else { + Err(format!("Bad signature on {:?}", hash)) + } + } +} + +/// A verifier for Aura blocks. +pub struct AuraVerifier { + config: Config, + client: Arc, +} + +impl Verifier for AuraVerifier where + C: Authorities + BlockImport + Send + Sync, + DigestItemFor: CompatibleDigestItem, +{ + fn verify( + &self, + origin: BlockOrigin, + header: B::Header, + _justification: Vec, + body: Option> + ) -> Result<(ImportBlock, Option>), String> { + let slot_now = slot_now(self.config.slot_duration) + .ok_or("System time is before UnixTime?".to_owned())?; + let hash = header.hash(); + let parent_hash = *header.parent_hash(); + let authorities = self.client.authorities(&BlockId::Hash(parent_hash)) + .map_err(|e| format!("Could not fetch authorities at {:?}: {:?}", parent_hash, e))?; + + // we add one to allow for some small drift. + // FIXME: in the future, alter this queue to allow deferring of headers + // https://github.com/paritytech/substrate/issues/1019 + let checked_header = check_header::(slot_now + 1, header, hash, &authorities[..])?; + match checked_header { + CheckedHeader::Checked(pre_header, slot_num, sig) => { + let item = >::aura_seal(slot_num, sig); + + debug!(target: "aura", "Checked {:?}; importing.", pre_header); + + let import_block = ImportBlock { + origin, + header: pre_header, + external_justification: Vec::new(), + post_runtime_digests: vec![item], + body, + finalized: false, + auxiliary: Vec::new(), + }; + + // FIXME: extract authorities - https://github.com/paritytech/substrate/issues/1019 + Ok((import_block, None)) + } + CheckedHeader::Deferred(a, b) => { + debug!(target: "aura", "Checking {:?} failed; {:?}, {:?}.", hash, a, b); + Err(format!("Header {:?} rejected: too far in the future", hash)) + } + } + } +} + +/// The Aura import queue type. +pub type AuraImportQueue = BasicQueue>; + +/// Start an import queue for the Aura consensus algorithm. +pub fn import_queue(config: Config, client: Arc) -> AuraImportQueue where + B: Block, + C: Authorities + BlockImport + Send + Sync, + DigestItemFor: CompatibleDigestItem, +{ + let verifier = Arc::new(AuraVerifier { config, client }); + BasicQueue::new(verifier) +} + + + +#[cfg(test)] +mod tests { + use super::*; + use consensus_common::NoNetwork as DummyOracle; + use network::test::*; + use network::test::{Block as TestBlock, PeersClient}; + use runtime_primitives::traits::Block as BlockT; + use network::ProtocolConfig; + use parking_lot::Mutex; + use tokio::runtime::current_thread; + use keyring::Keyring; + use client::BlockchainEvents; + use test_client; + + type Error = client::error::Error; + + type TestClient = client::Client; + + struct DummyFactory(Arc); + struct DummyProposer(u64, Arc); + + impl Environment for DummyFactory { + type Proposer = DummyProposer; + type Error = Error; + + fn init(&self, parent_header: &::Header, _authorities: &[AuthorityId], _sign_with: Arc) + -> Result + { + Ok(DummyProposer(parent_header.number + 1, self.0.clone())) + } + } + + impl Proposer for DummyProposer { + type Error = Error; + type Create = Result; + + fn propose(&self) -> Result { + self.1.new_block().unwrap().bake().map_err(|e| e.into()) + } + } + + const SLOT_DURATION: u64 = 1; + const TEST_ROUTING_INTERVAL: Duration = Duration::from_millis(50); + + pub struct AuraTestNet { + peers: Vec>>>, + started: bool + } + + impl TestNetFactory for AuraTestNet { + type Verifier = AuraVerifier; + + /// Create new test network with peers and given config. + fn from_config(_config: &ProtocolConfig) -> Self { + AuraTestNet { + peers: Vec::new(), + started: false + } + } + + fn make_verifier(&self, client: Arc, _cfg: &ProtocolConfig) + -> Arc + { + let config = Config { local_key: None, slot_duration: SLOT_DURATION }; + Arc::new(AuraVerifier { client, config }) + } + + fn peer(&self, i: usize) -> &Peer { + &self.peers[i] + } + + fn peers(&self) -> &Vec>> { + &self.peers + } + + fn mut_peers>>)>(&mut self, closure: F ) { + closure(&mut self.peers); + } + + fn started(&self) -> bool { + self.started + } + + fn set_started(&mut self, new: bool) { + self.started = new; + } + } + + #[test] + fn authoring_blocks() { + ::env_logger::init().ok(); + let mut net = AuraTestNet::new(3); + + net.start(); + + let peers = &[ + (0, Keyring::Alice), + (1, Keyring::Bob), + (2, Keyring::Charlie), + ]; + + let net = Arc::new(Mutex::new(net)); + let mut import_notifications = Vec::new(); + + let mut runtime = current_thread::Runtime::new().unwrap(); + for (peer_id, key) in peers { + let mut client = net.lock().peer(*peer_id).client().clone(); + let environ = Arc::new(DummyFactory(client.clone())); + import_notifications.push( + client.import_notification_stream() + .take_while(|n| { + Ok(!(n.origin != BlockOrigin::Own && n.header.number() < &5)) + }) + .for_each(move |_| Ok(())) + ); + let aura = start_aura( + Config { + local_key: Some(Arc::new(key.clone().into())), + slot_duration: SLOT_DURATION + }, + client, + environ.clone(), + DummyOracle, + ); + + runtime.spawn(aura); + } + + // wait for all finalized on each. + let wait_for = ::futures::future::join_all(import_notifications) + .map(|_| ()) + .map_err(|_| ()); + + let drive_to_completion = ::tokio::timer::Interval::new_interval(TEST_ROUTING_INTERVAL) + .for_each(move |_| { + net.lock().send_import_notifications(); + net.lock().sync(); + Ok(()) + }) + .map(|_| ()) + .map_err(|_| ()); + + runtime.block_on(wait_for.select(drive_to_completion).map_err(|_| ())).unwrap(); + } +} diff --git a/substrate/core/consensus/common/Cargo.toml b/substrate/core/consensus/common/Cargo.toml index ff5ebae0f8..08689721a0 100644 --- a/substrate/core/consensus/common/Cargo.toml +++ b/substrate/core/consensus/common/Cargo.toml @@ -4,5 +4,12 @@ version = "0.1.0" authors = ["Parity Technologies "] description = "Common utilities for substrate consensus" -[dev-dependencies] -substrate-primitives = { path= "../../primitives"} \ No newline at end of file +[dependencies] +substrate-primitives = { path= "../../primitives" } +error-chain = "0.12" +futures = "0.1" +sr-version = { path = "../../sr-version" } +sr-primitives = { path = "../../sr-primitives" } +tokio = "0.1.7" +parity-codec = "2.1" +parity-codec-derive = "2.0" diff --git a/substrate/core/consensus/common/src/block_import.rs b/substrate/core/consensus/common/src/block_import.rs new file mode 100644 index 0000000000..582886d827 --- /dev/null +++ b/substrate/core/consensus/common/src/block_import.rs @@ -0,0 +1,104 @@ + +use primitives::AuthorityId; +use runtime_primitives::traits::{Block as BlockT, DigestItemFor}; +use runtime_primitives::Justification; + +/// Block import result. +#[derive(Debug)] +pub enum ImportResult { + /// Added to the import queue. + Queued, + /// Already in the import queue. + AlreadyQueued, + /// Already in the blockchain. + AlreadyInChain, + /// Block or parent is known to be bad. + KnownBad, + /// Block parent is not in the chain. + UnknownParent, +} + +/// Block data origin. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum BlockOrigin { + /// Genesis block built into the client. + Genesis, + /// Block is part of the initial sync with the network. + NetworkInitialSync, + /// Block was broadcasted on the network. + NetworkBroadcast, + /// Block that was received from the network and validated in the consensus process. + ConsensusBroadcast, + /// Block that was collated by this node. + Own, + /// Block was imported from a file. + File, +} + +/// Data required to import a Block +pub struct ImportBlock { + /// Origin of the Block + pub origin: BlockOrigin, + /// The header, without consensus post-digests applied. This should be in the same + /// state as it comes out of the runtime. + /// + /// Consensus engines which alter the header (by adding post-runtime digests) + /// should strip those off in the initial verification process and pass them + /// via the `post_runtime_digests` field. During block authorship, they should + /// not be pushed to the header directly. + /// + /// The reason for this distinction is so the header can be directly + /// re-executed in a runtime that checks digest equivalence -- the + /// post-runtime digests are pushed back on after. + pub header: Block::Header, + /// Justification provided for this block from the outside:. + pub external_justification: Justification, + /// Digest items that have been added after the runtime for external + /// work, like a consensus signature. + pub post_runtime_digests: Vec>, + /// Block's body + pub body: Option>, + /// Is this block finalized already? + /// `true` implies instant finality. + pub finalized: bool, + /// Auxiliary consensus data produced by the block. + /// Contains a list of key-value pairs. If values are `None`, the keys + /// will be deleted. + pub auxiliary: Vec<(Vec, Option>)>, +} + +impl ImportBlock { + /// Deconstruct the justified header into parts. + pub fn into_inner(self) + -> ( + BlockOrigin, + ::Header, + Justification, + Vec>, + Option::Extrinsic>>, + bool, + Vec<(Vec, Option>)>, + ) { + ( + self.origin, + self.header, + self.external_justification, + self.post_runtime_digests, + self.body, + self.finalized, + self.auxiliary, + ) + } +} + + + +/// Block import trait. +pub trait BlockImport { + type Error: ::std::error::Error + Send + 'static; + /// Import a Block alongside the new authorities valid form this block forward + fn import_block(&self, + block: ImportBlock, + new_authorities: Option> + ) -> Result; +} \ No newline at end of file diff --git a/substrate/core/consensus/common/src/error.rs b/substrate/core/consensus/common/src/error.rs new file mode 100644 index 0000000000..ccf57adb9f --- /dev/null +++ b/substrate/core/consensus/common/src/error.rs @@ -0,0 +1,88 @@ +// Copyright 2017-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 . + +//! Error types in Consensus +use runtime_version::RuntimeVersion; + +error_chain! { + errors { + /// Missing state at block with given descriptor. + StateUnavailable(b: String) { + description("State missing at given block."), + display("State unavailable at block {}", b), + } + + /// I/O terminated unexpectedly + IoTerminated { + description("I/O terminated unexpectedly."), + display("I/O terminated unexpectedly."), + } + + /// Unable to schedule wakeup. + FaultyTimer(e: ::tokio::timer::Error) { + description("Timer error"), + display("Timer error: {}", e), + } + + /// Unable to propose a block. + CannotPropose { + description("Unable to create block proposal."), + display("Unable to create block proposal."), + } + + /// Error checking signature + InvalidSignature(s: ::primitives::ed25519::Signature, a: ::primitives::AuthorityId) { + description("Message signature is invalid"), + display("Message signature {:?} by {:?} is invalid.", s, a), + } + + /// Account is not an authority. + InvalidAuthority(a: ::primitives::AuthorityId) { + description("Message sender is not a valid authority"), + display("Message sender {:?} is not a valid authority.", a), + } + + /// Authoring interface does not match the runtime. + IncompatibleAuthoringRuntime(native: RuntimeVersion, on_chain: RuntimeVersion) { + description("Authoring for current runtime is not supported"), + display("Authoring for current runtime is not supported. Native ({}) cannot author for on-chain ({}).", native, on_chain), + } + + /// Authoring interface does not match the runtime. + RuntimeVersionMissing { + description("Current runtime has no version"), + display("Authoring for current runtime is not supported since it has no version."), + } + + /// Authoring interface does not match the runtime. + NativeRuntimeMissing { + description("This build has no native runtime"), + display("Authoring in current build is not supported since it has no runtime."), + } + + /// Justification requirements not met. + InvalidJustification { + description("Invalid justification"), + display("Invalid justification."), + } + + /// Some other error. + Other(e: Box<::std::error::Error + Send>) { + description("Other error") + display("Other error: {}", e.description()) + } + } +} diff --git a/substrate/node/consensus/src/evaluation.rs b/substrate/core/consensus/common/src/evaluation.rs similarity index 73% rename from substrate/node/consensus/src/evaluation.rs rename to substrate/core/consensus/common/src/evaluation.rs index c21c202e96..db35e2f411 100644 --- a/substrate/node/consensus/src/evaluation.rs +++ b/substrate/core/consensus/common/src/evaluation.rs @@ -18,11 +18,10 @@ use super::MAX_TRANSACTIONS_SIZE; -use codec::{Decode, Encode}; -use node_runtime::{Block as GenericBlock}; -use node_primitives::{Hash, BlockNumber}; +use codec::Encode; use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, As}; +type BlockNumber = u64; error_chain! { errors { @@ -30,13 +29,13 @@ error_chain! { description("Proposal provided not a block."), display("Proposal provided not a block."), } - WrongParentHash(expected: Hash, got: Hash) { + WrongParentHash(expected: String, got: String) { description("Proposal had wrong parent hash."), display("Proposal had wrong parent hash. Expected {:?}, got {:?}", expected, got), } WrongNumber(expected: BlockNumber, got: BlockNumber) { description("Proposal had wrong number."), - display("Proposal had wrong number. Expected {:?}, got {:?}", expected, got), + display("Proposal had wrong number. Expected {}, got {}", expected, got), } ProposalTooLarge(size: usize) { description("Proposal exceeded the maximum size."), @@ -50,20 +49,17 @@ error_chain! { /// Attempt to evaluate a substrate block as a node block, returning error /// upon any initial validity checks failing. -pub fn evaluate_initial( +pub fn evaluate_initial( proposal: &Block, - parent_hash: &Hash, + parent_hash: &::Hash, parent_number: <::Header as HeaderT>::Number, -) -> Result<()> -where - Hash: PartialEq<<::Header as HeaderT>::Hash>, - Hash: Into + Clone, -{ +) -> Result<()> { + let encoded = Encode::encode(proposal); - let proposal = GenericBlock::decode(&mut &encoded[..]) + let proposal = Block::decode(&mut &encoded[..]) .ok_or_else(|| ErrorKind::BadProposalFormat)?; - let transactions_size = proposal.extrinsics.iter().fold(0, |a, tx| { + let transactions_size = proposal.extrinsics().iter().fold(0, |a, tx| { a + Encode::encode(tx).len() }); @@ -72,11 +68,14 @@ where } if *parent_hash != *proposal.header().parent_hash() { - bail!(ErrorKind::WrongParentHash((*parent_hash).clone().into(), proposal.header.parent_hash)); + bail!(ErrorKind::WrongParentHash( + format!("{:?}", *parent_hash), + format!("{:?}", proposal.header().parent_hash()) + )); } - if parent_number.as_() + 1 != *proposal.header().number() { - bail!(ErrorKind::WrongNumber(parent_number.as_() + 1, proposal.header.number)); + if parent_number.as_() + 1 != proposal.header().number().as_() { + bail!(ErrorKind::WrongNumber(parent_number.as_() + 1, proposal.header().number().as_())); } Ok(()) diff --git a/substrate/core/consensus/common/src/lib.rs b/substrate/core/consensus/common/src/lib.rs index fde9d20ec9..12d5cfa70d 100644 --- a/substrate/core/consensus/common/src/lib.rs +++ b/substrate/core/consensus/common/src/lib.rs @@ -15,22 +15,118 @@ // along with Substrate Consensus Common. If not, see . // tag::description[] -//! Tracks offline validators. +//! Consensus basics and common features // end::description[] +// This provides "unused" building blocks to other crates #![allow(dead_code)] -#![cfg(feature="rhd")] +// our error-chain could potentially blow up otherwise +#![recursion_limit="128"] extern crate substrate_primitives as primitives; +extern crate futures; +extern crate sr_version as runtime_version; +extern crate sr_primitives as runtime_primitives; +extern crate tokio; -use primitives::{generic::BlockId, Justification}; -use primitives::traits::{Block, Header}; +extern crate parity_codec as codec; +#[macro_use] +extern crate parity_codec_derive; -/// Block import trait. -pub trait BlockImport { - /// Import a block alongside its corresponding justification. - fn import_block(&self, block: B, justification: Justification, authorities: &[AuthorityId]) -> bool; +#[macro_use] +extern crate error_chain; + +use std::sync::Arc; + +use primitives::{ed25519, AuthorityId}; +use runtime_primitives::generic::BlockId; +use runtime_primitives::traits::Block; +use futures::prelude::*; + +pub mod offline_tracker; +pub mod error; +mod block_import; +pub mod evaluation; + +// block size limit. +const MAX_TRANSACTIONS_SIZE: usize = 4 * 1024 * 1024; + +pub use self::error::{Error, ErrorKind}; +pub use block_import::{BlockImport, ImportBlock, BlockOrigin, ImportResult}; + +/// Trait for getting the authorities at a given block. +pub trait Authorities { + type Error: ::std::error::Error + Send + 'static; /// Get the authorities at the given block. + fn authorities(&self, at: &BlockId) -> Result, Self::Error>; } -pub mod offline_tracker; \ No newline at end of file +/// Environment producer for a Consensus instance. Creates proposer instance and communication streams. +pub trait Environment { + /// The proposer type this creates. + type Proposer: Proposer; + /// Error which can occur upon creation. + type Error: From; + + /// Initialize the proposal logic on top of a specific header. Provide + /// the authorities at that header, and a local key to sign any additional + /// consensus messages with as well. + fn init(&self, parent_header: &B::Header, authorities: &[AuthorityId], sign_with: Arc) + -> Result; +} + +/// Logic for a proposer. +/// +/// This will encapsulate creation and evaluation of proposals at a specific +/// block. +pub trait Proposer { + /// Error type which can occur when proposing or evaluating. + type Error: From + ::std::fmt::Debug + 'static; + /// Future that resolves to a committed proposal. + type Create: IntoFuture; + /// Create a proposal. + fn propose(&self) -> Self::Create; +} + +/// Inherent data to include in a block. +#[derive(Encode, Decode)] +pub struct InherentData { + /// Current timestamp. + pub timestamp: u64, + /// Indices of offline validators. + pub offline_indices: Vec, +} + +impl InherentData { + /// Create a new `InherentData` instance. + pub fn new(timestamp: u64, offline_indices: Vec) -> Self { + Self { + timestamp, + offline_indices + } + } +} + +/// An oracle for when major synchronization work is being undertaken. +/// +/// Generally, consensus authoring work isn't undertaken while well behind +/// the head of the chain. +pub trait SyncOracle { + /// Whether the synchronization service is undergoing major sync. + /// Returns true if so. + fn is_major_syncing(&self) -> bool; +} + +/// A synchronization oracle for when there is no network. +#[derive(Clone, Copy, Debug)] +pub struct NoNetwork; + +impl SyncOracle for NoNetwork { + fn is_major_syncing(&self) -> bool { false } +} + +impl SyncOracle for Arc { + fn is_major_syncing(&self) -> bool { + T::is_major_syncing(&*self) + } +} diff --git a/substrate/core/consensus/common/src/offline_tracker.rs b/substrate/core/consensus/common/src/offline_tracker.rs index 18845dd68b..bd8eab8b1b 100644 --- a/substrate/core/consensus/common/src/offline_tracker.rs +++ b/substrate/core/consensus/common/src/offline_tracker.rs @@ -16,7 +16,7 @@ //! Tracks offline validators. -use node_primitives::AccountId; +use primitives::AuthorityId; use std::collections::HashMap; use std::time::{Instant, Duration}; @@ -56,7 +56,7 @@ impl Observed { /// Tracks offline validators and can issue a report for those offline. pub struct OfflineTracker { - observed: HashMap, + observed: HashMap, } impl OfflineTracker { @@ -66,7 +66,7 @@ impl OfflineTracker { } /// Note new consensus is starting with the given set of validators. - pub fn note_new_block(&mut self, validators: &[AccountId]) { + pub fn note_new_block(&mut self, validators: &[AuthorityId]) { use std::collections::HashSet; let set: HashSet<_> = validators.iter().cloned().collect(); @@ -74,14 +74,14 @@ impl OfflineTracker { } /// Note that a round has ended. - pub fn note_round_end(&mut self, validator: AccountId, was_online: bool) { + pub fn note_round_end(&mut self, validator: AuthorityId, was_online: bool) { self.observed.entry(validator) .or_insert_with(Observed::new) .note_round_end(was_online); } /// Generate a vector of indices for offline account IDs. - pub fn reports(&self, validators: &[AccountId]) -> Vec { + pub fn reports(&self, validators: &[AuthorityId]) -> Vec { validators.iter() .enumerate() .filter_map(|(i, v)| if self.is_online(v) { @@ -93,7 +93,7 @@ impl OfflineTracker { } /// Whether reports on a validator set are consistent with our view of things. - pub fn check_consistency(&self, validators: &[AccountId], reports: &[u32]) -> bool { + pub fn check_consistency(&self, validators: &[AuthorityId], reports: &[u32]) -> bool { reports.iter().cloned().all(|r| { let v = match validators.get(r as usize) { Some(v) => v, @@ -106,7 +106,7 @@ impl OfflineTracker { }) } - fn is_online(&self, v: &AccountId) -> bool { + fn is_online(&self, v: &AuthorityId) -> bool { self.observed.get(v).map(Observed::is_active).unwrap_or(true) } } diff --git a/substrate/core/consensus/rhd/Cargo.toml b/substrate/core/consensus/rhd/Cargo.toml index f5b928c429..dce6a6d7f4 100644 --- a/substrate/core/consensus/rhd/Cargo.toml +++ b/substrate/core/consensus/rhd/Cargo.toml @@ -6,30 +6,33 @@ description = "Rhododendron Round-Based consensus-algorithm for substrate" [dependencies] futures = "0.1.17" -parity-codec = { version = "1.1" } +parity-codec = { version = "2.1" } parity-codec-derive = { version = "2.0" } substrate-primitives = { path = "../../primitives" } +substrate-consensus-common = { path = "../common" } +substrate-client = { path = "../../client" } +substrate-transaction-pool = { path = "../../transaction-pool" } srml-support = { path = "../../../srml/support" } +srml-system = { path = "../../../srml/system" } +srml-consensus = { path = "../../../srml/consensus" } sr-primitives = { path = "../../sr-primitives" } sr-version = { path = "../../sr-version" } sr-io = { path = "../../sr-io" } -srml-consensus = { path = "../../../srml/consensus" } tokio = "0.1.7" parking_lot = "0.4" error-chain = "0.12" -log = "0.3" -rhododendron = { git = "https://github.com/paritytech/rhododendron.git", features = ["codec"] } -serde = { version = "1.0", features = ["derive"] } +log = "0.4" +rhododendron = { version = "0.4.0", features = ["codec"] } +exit-future = "0.1" + [dev-dependencies] substrate-keyring = { path = "../../keyring" } substrate-executor = { path = "../../executor" } - [features] default = ["std"] std = [ - "serde/std", "substrate-primitives/std", "srml-support/std", "sr-primitives/std", diff --git a/substrate/core/consensus/rhd/src/error.rs b/substrate/core/consensus/rhd/src/error.rs index 806ba5624a..c18c36f679 100644 --- a/substrate/core/consensus/rhd/src/error.rs +++ b/substrate/core/consensus/rhd/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2017-2018 Parity Technologies (UK) Ltd. +// Copyright 2018 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify @@ -14,81 +14,44 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Error types in the BFT service. -use runtime_version::RuntimeVersion; +//! Error types in the rhododendron Consensus service. +use consensus::error::{Error as CommonError, ErrorKind as CommonErrorKind}; +use primitives::AuthorityId; +use client; error_chain! { + links { + Client(client::error::Error, client::error::ErrorKind); + Common(CommonError, CommonErrorKind); + } errors { - /// Missing state at block with given descriptor. - StateUnavailable(b: String) { - description("State missing at given block."), - display("State unavailable at block {}", b), + NotValidator(id: AuthorityId) { + description("Local account ID not a validator at this block."), + display("Local account ID ({:?}) not a validator at this block.", id), } - - /// I/O terminated unexpectedly - IoTerminated { - description("I/O terminated unexpectedly."), - display("I/O terminated unexpectedly."), + PrematureDestruction { + description("Proposer destroyed before finishing proposing or evaluating"), + display("Proposer destroyed before finishing proposing or evaluating"), } - - /// Unable to schedule wakeup. - FaultyTimer(e: ::tokio::timer::Error) { - description("Timer error"), - display("Timer error: {}", e), + Timer(e: ::tokio::timer::Error) { + description("Failed to register or resolve async timer."), + display("Timer failed: {}", e), } - - /// Unable to propose a block. - CannotPropose { - description("Unable to create block proposal."), - display("Unable to create block proposal."), - } - - /// Error checking signature - InvalidSignature(s: ::primitives::ed25519::Signature, a: ::primitives::AuthorityId) { - description("Message signature is invalid"), - display("Message signature {:?} by {:?} is invalid.", s, a), - } - - /// Account is not an authority. - InvalidAuthority(a: ::primitives::AuthorityId) { - description("Message sender is not a valid authority"), - display("Message sender {:?} is not a valid authority.", a), - } - - /// Authoring interface does not match the runtime. - IncompatibleAuthoringRuntime(native: RuntimeVersion, on_chain: RuntimeVersion) { - description("Authoring for current runtime is not supported"), - display("Authoring for current runtime is not supported. Native ({}) cannot author for on-chain ({}).", native, on_chain), - } - - /// Authoring interface does not match the runtime. - RuntimeVersionMissing { - description("Current runtime has no version"), - display("Authoring for current runtime is not supported since it has no version."), - } - - /// Authoring interface does not match the runtime. - NativeRuntimeMissing { - description("This build has no native runtime"), - display("Authoring in current build is not supported since it has no runtime."), - } - - /// Justification requirements not met. - InvalidJustification { - description("Invalid justification"), - display("Invalid justification."), - } - - /// Some other error. - Other(e: Box<::std::error::Error + Send>) { - description("Other error") - display("Other error: {}", e.description()) + Executor(e: ::futures::future::ExecuteErrorKind) { + description("Unable to dispatch agreement future"), + display("Unable to dispatch agreement future: {:?}", e), } } } impl From<::rhododendron::InputStreamConcluded> for Error { - fn from(_: ::rhododendron::InputStreamConcluded) -> Error { - ErrorKind::IoTerminated.into() + fn from(_: ::rhododendron::InputStreamConcluded) -> Self { + CommonErrorKind::IoTerminated.into() } } + +impl From for Error { + fn from(e: CommonErrorKind) -> Self { + CommonError::from(e).into() + } +} \ No newline at end of file diff --git a/substrate/core/consensus/rhd/src/lib.rs b/substrate/core/consensus/rhd/src/lib.rs index 0bb0cbc3c6..b99ea4a24c 100644 --- a/substrate/core/consensus/rhd/src/lib.rs +++ b/substrate/core/consensus/rhd/src/lib.rs @@ -32,60 +32,74 @@ //! set for this block height. // end::description[] -#![cfg(feature = "rhd")] - -#![recursion_limit="128"] +#![cfg(feature="rhd")] +// FIXME: doesn't compile - https://github.com/paritytech/substrate/issues/1020 extern crate parity_codec as codec; extern crate substrate_primitives as primitives; +extern crate substrate_client as client; +extern crate substrate_consensus_common as consensus; +extern crate substrate_transaction_pool as transaction_pool; +extern crate srml_system; extern crate srml_support as runtime_support; extern crate sr_primitives as runtime_primitives; extern crate sr_version as runtime_version; extern crate sr_io as runtime_io; -extern crate tokio; -#[cfg(test)] -extern crate substrate_keyring as keyring; extern crate parking_lot; extern crate rhododendron; +extern crate futures; +extern crate exit_future; +extern crate tokio; #[macro_use] extern crate log; - -extern crate futures; - #[macro_use] extern crate error_chain; -#[macro_use] -extern crate serde; - #[macro_use] extern crate parity_codec_derive; - -pub mod error; +#[cfg(test)] +extern crate substrate_keyring; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::time::{Instant, Duration}; +use std::time::{self, Instant, Duration}; -use codec::Encode; -use runtime_primitives::{generic::BlockId, Justification}; +use codec::{Decode, Encode}; +use consensus::offline_tracker::OfflineTracker; +use consensus::error::{ErrorKind as CommonErrorKind}; +use consensus::{Authorities, BlockImport, Environment, Proposer as BaseProposer}; +use client::{Client as SubstrateClient, CallExecutor}; +use client::runtime_api::{Core, BlockBuilder as BlockBuilderAPI, OldTxQueue, BlockBuilderError}; +use runtime_primitives::generic::{BlockId, Era, ImportResult, ImportBlock, BlockOrigin}; use runtime_primitives::traits::{Block, Header}; -use primitives::{AuthorityId, ed25519, ed25519::LocalizedSignature}; +use runtime_primitives::traits::{Block as BlockT, Hash as HashT, Header as HeaderT, As, BlockNumberToHash}; +use runtime_primitives::Justification; +use primitives::{AuthorityId, ed25519, Blake2Hasher, ed25519::LocalizedSignature}; +use srml_system::Trait as SystemT; -use futures::{Async, Stream, Sink, Future, IntoFuture}; +use node_runtime::Runtime; +use transaction_pool::txpool::{self, Pool as TransactionPool}; + +use futures::prelude::*; +use futures::future; use futures::sync::oneshot; +use tokio::runtime::TaskExecutor; use tokio::timer::Delay; -use parking_lot::Mutex; +use parking_lot::{RwLock, Mutex}; -pub use rhododendron::{InputStreamConcluded, AdvanceRoundReason, - Message as RhdMessage, Vote as RhdMessageVote}; -pub use error::{Error, ErrorKind}; +pub use rhododendron::{ + self, InputStreamConcluded, AdvanceRoundReason, Message as RhdMessage, + Vote as RhdMessageVote, Communication as RhdCommunication, +}; +pub use self::error::{Error, ErrorKind}; // pub mod misbehaviour_check; +mod error; +mod service; // statuses for an agreement mod status { @@ -94,6 +108,10 @@ mod status { pub const GOOD: usize = 2; } +pub type Timestamp = u64; + +pub type AccountId = ::primitives::H256; + /// Localized message type. pub type LocalizedMessage = rhododendron::LocalizedMessage< B, @@ -102,8 +120,6 @@ pub type LocalizedMessage = rhododendron::LocalizedMessage< LocalizedSignature >; - - /// Justification of some hash. pub struct RhdJustification(rhododendron::Justification); @@ -111,11 +127,12 @@ pub struct RhdJustification(rhododendron::Justification(rhododendron::PrepareJustification); /// Unchecked justification. +#[derive(Encode, Decode)] pub struct UncheckedJustification(rhododendron::UncheckedJustification); impl UncheckedJustification { /// Create a new, unchecked justification. - pub fn new(digest: H, signatures: Vec, round_number: usize) -> Self { + pub fn new(digest: H, signatures: Vec, round_number: u32) -> Self { UncheckedJustification(rhododendron::UncheckedJustification { digest, signatures, @@ -124,13 +141,20 @@ impl UncheckedJustification { } } -impl Into for RhdJustification { - fn into(self) -> Justification { - let p : Justification = UncheckedJustification(self.0.uncheck()).into(); - p +impl UncheckedJustification { + /// Decode a justification. + pub fn decode_justification(justification: Justification) -> Option { + let inner: rhododendron::UncheckedJustification<_, _> = Decode::decode(&mut &justification[..])?; + + Some(UncheckedJustification(inner)) } } +impl Into for UncheckedJustification { + fn into(self) -> Justification { + self.0.encode() + } +} impl From> for UncheckedJustification { fn from(inner: rhododendron::UncheckedJustification) -> Self { @@ -138,29 +162,6 @@ impl From> for Un } } -impl From for UncheckedJustification { - fn from(just: Justification) -> Self { - UncheckedJustification(rhododendron::UncheckedJustification { - round_number: just.round_number as usize, - digest: just.hash, - signatures: just.signatures.into_iter().map(|(from, sig)| LocalizedSignature { - signer: from.into(), - signature: sig, - }).collect(), - }) - } -} - -impl Into for UncheckedJustification { - fn into(self) -> Justification { - Justification { - round_number: self.0.round_number as u32, - hash: self.0.digest, - signatures: self.0.signatures.into_iter().map(|s| (s.signer.into(), s.signature)).collect(), - } - } -} - /// Result of a committed round of BFT pub type Committed = rhododendron::Committed::Hash, LocalizedSignature>; @@ -170,59 +171,72 @@ pub type Communication = rhododendron::Communication::Hash, A /// Misbehavior observed from BFT participants. pub type Misbehavior = rhododendron::Misbehavior; -/// Environment producer for a BFT instance. Creates proposer instance and communication streams. -pub trait Environment { - /// The proposer type this creates. - type Proposer: Proposer; - /// The input stream type. - type Input: Stream, Error=>::Error>; - /// The output stream type. - type Output: Sink, SinkError=>::Error>; - /// Error which can occur upon creation. - type Error: From; +/// Shared offline validator tracker. +pub type SharedOfflineTracker = Arc>; - /// Initialize the proposal logic on top of a specific header. - /// Produces the proposer and message streams for this instance of BFT agreement. - // TODO: provide state context explicitly? - fn init(&self, parent_header: &B::Header, authorities: &[AuthorityId], sign_with: Arc) - -> Result<(Self::Proposer, Self::Input, Self::Output), Self::Error>; -} - -/// Logic for a proposer. -/// -/// This will encapsulate creation and evaluation of proposals at a specific -/// block. -pub trait Proposer { - /// Error type which can occur when proposing or evaluating. - type Error: From + From + ::std::fmt::Debug + 'static; - /// Future that resolves to a committed proposal. - type Create: IntoFuture; - /// Future that resolves when a proposal is evaluated. - type Evaluate: IntoFuture; - - /// Create a proposal. - fn propose(&self) -> Self::Create; - - /// Evaluate proposal. True means valid. - fn evaluate(&self, proposal: &B) -> Self::Evaluate; - - /// Import witnessed misbehavior. +/// A proposer for a rhododendron instance. This must implement the base proposer logic. +pub trait LocalProposer: BaseProposer { + /// Import witnessed rhododendron misbehavior. fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, Misbehavior)>); /// Determine the proposer for a given round. This should be a deterministic function /// with consistent results across all authorities. - fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId; + fn round_proposer(&self, round_number: u32, authorities: &[AuthorityId]) -> AuthorityId; /// Hook called when a BFT round advances without a proposal. - fn on_round_end(&self, _round_number: usize, _proposed: bool) { } + fn on_round_end(&self, _round_number: u32, _proposed: bool) { } } -/// Trait for getting the authorities at a given block. -pub trait Authorities { - /// Get the authorities at the given block. - fn authorities(&self, at: &BlockId) -> Result, Error>; + +/// Build new blocks. +pub trait BlockBuilder { + /// Push an extrinsic onto the block. Fails if the extrinsic is invalid. + fn push_extrinsic(&mut self, extrinsic: ::Extrinsic) -> Result<(), Error>; } +/// Local client abstraction for the consensus. +pub trait AuthoringApi: + Send + + Sync + + BlockBuilderAPI<::Block, Error=::Error> + + Core<::Block, AuthorityId, Error=::Error> + + OldTxQueue<::Block, Error=::Error> +{ + /// The block used for this API type. + type Block: BlockT; + /// The error used by this API type. + type Error: std::error::Error; + + /// Build a block on top of the given, with inherent extrinsics pre-pushed. + fn build_block) -> ()>( + &self, + at: &BlockId, + inherent_data: InherentData, + build_ctx: F, + ) -> Result; +} + +/// A long-lived network which can create BFT message routing processes on demand. +pub trait Network { + /// The block used for this API type. + type Block: BlockT; + /// The input stream of BFT messages. Should never logically conclude. + type Input: Stream,Error=Error>; + /// The output sink of BFT messages. Messages sent here should eventually pass to all + /// current authorities. + type Output: Sink,SinkError=Error>; + + /// Instantiate input and output streams. + fn communication_for( + &self, + validators: &[AuthorityId], + local_id: AuthorityId, + parent_hash: ::Hash, + task_executor: TaskExecutor + ) -> (Self::Input, Self::Output); +} + + // caches the round number to start at if we end up with BFT consensus on the same // parent hash more than once (happens if block is bad). // @@ -231,7 +245,7 @@ pub trait Authorities { #[derive(Debug)] struct RoundCache { hash: Option, - start_round: usize, + start_round: u32, } /// Instance of BFT agreement. @@ -244,19 +258,19 @@ struct BftInstance { proposer: P, } -impl> BftInstance +impl> BftInstance where B: Clone + Eq, B::Hash: ::std::hash::Hash { - fn round_timeout_duration(&self, round: usize) -> Duration { + fn round_timeout_duration(&self, round: u32) -> Duration { // 2^(min(6, x/8)) * 10 // Grows exponentially starting from 10 seconds, capped at 640 seconds. - const ROUND_INCREMENT_STEP: usize = 8; + const ROUND_INCREMENT_STEP: u32 = 8; let round = round / ROUND_INCREMENT_STEP; - let round = ::std::cmp::min(6, round) as u32; + let round = ::std::cmp::min(6, round); let timeout = 1u64.checked_shl(round) .unwrap_or_else(u64::max_value) @@ -265,7 +279,7 @@ impl> BftInstance Duration::from_secs(timeout) } - fn update_round_cache(&self, current_round: usize) { + fn update_round_cache(&self, current_round: u32) { let mut cache = self.cache.lock(); if cache.hash.as_ref() == Some(&self.parent_hash) { cache.start_round = current_round + 1; @@ -273,7 +287,7 @@ impl> BftInstance } } -impl> rhododendron::Context for BftInstance +impl> rhododendron::Context for BftInstance where B: Clone + Eq, B::Hash: ::std::hash::Hash, @@ -303,7 +317,7 @@ impl> rhododendron::Context for BftInstance sign_message(message, &*self.key, self.parent_hash.clone()) } - fn round_proposer(&self, round: usize) -> AuthorityId { + fn round_proposer(&self, round: u32) -> AuthorityId { self.proposer.round_proposer(round, &self.authorities[..]) } @@ -311,10 +325,10 @@ impl> rhododendron::Context for BftInstance self.proposer.evaluate(proposal).into_future() } - fn begin_round_timeout(&self, round: usize) -> Self::RoundTimeout { + fn begin_round_timeout(&self, round: u32) -> Self::RoundTimeout { let timeout = self.round_timeout_duration(round); let fut = Delay::new(Instant::now() + timeout) - .map_err(|e| Error::from(ErrorKind::FaultyTimer(e))) + .map_err(|e| Error::from(CommonErrorKind::FaultyTimer(e))) .map_err(Into::into); Box::new(fut) @@ -322,9 +336,9 @@ impl> rhododendron::Context for BftInstance fn on_advance_round( &self, - accumulator: &::rhododendron::Accumulator, - round: usize, - next_round: usize, + accumulator: &rhododendron::Accumulator, + round: u32, + next_round: u32, reason: AdvanceRoundReason, ) { use std::collections::HashSet; @@ -334,12 +348,12 @@ impl> rhododendron::Context for BftInstance .collect::>(); let round_timeout = self.round_timeout_duration(next_round); - debug!(target: "bft", "Advancing to round {} from {}", next_round, round); - debug!(target: "bft", "Participating authorities: {:?}", + debug!(target: "rhd", "Advancing to round {} from {}", next_round, round); + debug!(target: "rhd", "Participating authorities: {:?}", collect_pubkeys(accumulator.participants())); - debug!(target: "bft", "Voting authorities: {:?}", + debug!(target: "rhd", "Voting authorities: {:?}", collect_pubkeys(accumulator.voters())); - debug!(target: "bft", "Round {} should end in at most {} seconds from now", next_round, round_timeout.as_secs()); + debug!(target: "rhd", "Round {} should end in at most {} seconds from now", next_round, round_timeout.as_secs()); self.update_round_cache(next_round); @@ -354,9 +368,10 @@ impl> rhododendron::Context for BftInstance pub struct BftFuture where B: Block + Clone + Eq, B::Hash: ::std::hash::Hash, - P: Proposer, - InStream: Stream, Error=P::Error>, - OutSink: Sink, SinkError=P::Error>, + P: LocalProposer, + P: BaseProposer, + InStream: Stream, Error=Error>, + OutSink: Sink, SinkError=Error>, { inner: rhododendron::Agreement, InStream, OutSink>, status: Arc, @@ -367,11 +382,11 @@ pub struct BftFuture where impl Future for BftFuture where B: Block + Clone + Eq, B::Hash: ::std::hash::Hash, - P: Proposer, - P::Error: ::std::fmt::Display, + P: LocalProposer, + P: BaseProposer, I: BlockImport, - InStream: Stream, Error=P::Error>, - OutSink: Sink, SinkError=P::Error>, + InStream: Stream, Error=Error>, + OutSink: Sink, SinkError=Error>, { type Item = (); type Error = (); @@ -383,7 +398,6 @@ impl Future for BftFuture false, }; - // TODO: handle and log this error in a way which isn't noisy on exit. let committed = match self.inner.poll().map_err(|_| ()) { Ok(Async::Ready(x)) => x, Ok(Async::NotReady) => @@ -398,23 +412,36 @@ impl Future for BftFuture { + warn!(target: "rhd", "Error importing block {:?} in round #{}: {:?}", + hash, committed.round_number, e); + status::BAD + } + Ok(ImportResult::KnownBad) => { + warn!(target: "rhd", "{:?} was bad block agreed on in round #{}", + hash, committed.round_number); + status::BAD + } + _ => status::GOOD + }; + + self.status.store(new_status, Ordering::Release); - if !import_ok { - warn!(target: "bft", "{:?} was bad block agreed on in round #{}", - hash, committed.round_number); - self.status.store(status::BAD, Ordering::Release); - } else { - self.status.store(status::GOOD, Ordering::Release); - } } else { // assume good unless we received the proposal. self.status.store(status::GOOD, Ordering::Release); @@ -429,9 +456,10 @@ impl Future for BftFuture Drop for BftFuture where B: Block + Clone + Eq, B::Hash: ::std::hash::Hash, - P: Proposer, - InStream: Stream, Error=P::Error>, - OutSink: Sink, SinkError=P::Error>, + P: LocalProposer, + P: BaseProposer, + InStream: Stream, Error=Error>, + OutSink: Sink, SinkError=Error>, { fn drop(&mut self) { // TODO: have a trait member to pass misbehavior reports into. @@ -476,10 +504,10 @@ impl BftService where B: Block + Clone + Eq, P: Environment, - >::Error: ::std::fmt::Display, + P::Proposer: LocalProposer, + P::Proposer: BaseProposer, I: BlockImport + Authorities, { - /// Create a new service instance. pub fn new(client: Arc, key: Arc, factory: P) -> BftService { BftService { @@ -502,20 +530,29 @@ impl BftService } /// Signal that a valid block with the given header has been imported. + /// Provide communication streams that are localized to this block. + /// It's recommended to use the communication primitives provided by this + /// module for signature checking and decoding. See `CheckedStream` and + /// `SigningSink` for more details. + /// + /// Messages received on the stream that don't match the expected format + /// will be dropped. /// /// If the local signing key is an authority, this will begin the consensus process to build a /// block on top of it. If the executor fails to run the future, an error will be returned. /// Returns `None` if the agreement on the block with given parent is already in progress. - pub fn build_upon(&self, header: &B::Header) + pub fn build_upon(&self, header: &B::Header, input: In, output: Out) -> Result>::Proposer, I, -

>::Input, -

>::Output, + In, + Out, >>, P::Error> where + In: Stream, Error=Error>, + Out: Sink, SinkError=Error>, { let hash = header.hash(); @@ -527,22 +564,23 @@ impl BftService return Ok(None) } - let authorities = self.client.authorities(&BlockId::Hash(hash.clone()))?; + let authorities = self.client.authorities(&BlockId::Hash(hash.clone())) + .map_err(|e| CommonErrorKind::Other(Box::new(e)).into())?; let n = authorities.len(); let max_faulty = max_faulty_of(n); - trace!(target: "bft", "Initiating agreement on top of #{}, {:?}", header.number(), hash); - trace!(target: "bft", "max_faulty_of({})={}", n, max_faulty); + trace!(target: "rhd", "Initiating agreement on top of #{}, {:?}", header.number(), hash); + trace!(target: "rhd", "max_faulty_of({})={}", n, max_faulty); let local_id = self.local_id(); if !authorities.contains(&local_id) { // cancel current agreement live_agreement.take(); - Err(ErrorKind::InvalidAuthority(local_id).into())?; + Err(CommonErrorKind::InvalidAuthority(local_id).into())?; } - let (proposer, input, output) = self.factory.init(header, &authorities, self.key.clone())?; + let proposer = self.factory.init(header, &authorities, self.key.clone())?; let bft_instance = BftInstance { proposer, @@ -564,9 +602,9 @@ impl BftService // fast forward round number if necessary. { let mut cache = self.round_cache.lock(); - trace!(target: "bft", "Round cache: {:?}", &*cache); + trace!(target: "rhd", "Round cache: {:?}", &*cache); if cache.hash.as_ref() == Some(&hash) { - trace!(target: "bft", "Fast-forwarding to round {}", cache.start_round); + trace!(target: "rhd", "Fast-forwarding to round {}", cache.start_round); let start_round = cache.start_round; cache.start_round += 1; @@ -622,6 +660,103 @@ impl BftService } } +/// Stream that decodes rhododendron messages and checks signatures. +/// +/// This stream is localized to a specific parent block-hash, as all messages +/// will be signed in a way that accounts for it. When using this with +/// `BftService::build_upon`, the user should take care to use the same hash as for that. +pub struct CheckedStream { + inner: S, + local_id: AuthorityId, + authorities: Vec, + parent_hash: B::Hash, +} + +impl CheckedStream { + /// Construct a new checked stream. + pub fn new( + inner: S, + local_id: AuthorityId, + authorities: Vec, + parent_hash: B::Hash, + ) -> Self { + CheckedStream { + inner, + local_id, + authorities, + parent_hash, + } + } +} + +impl>> Stream for CheckedStream + where S::Error: From, +{ + type Item = Communication; + type Error = S::Error; + + fn poll(&mut self) -> Poll, Self::Error> { + use rhododendron::LocalizedMessage as RhdLocalized; + loop { + match self.inner.poll()? { + Async::Ready(Some(item)) => { + let comms: Communication = match Decode::decode(&mut &item[..]) { + Some(x) => x, + None => continue, + }; + + match comms { + RhdCommunication::Auxiliary(prepare_just) => { + let checked = check_prepare_justification::( + &self.authorities, + self.parent_hash, + UncheckedJustification(prepare_just.uncheck()), + ); + if let Ok(checked) = checked { + return Ok(Async::Ready( + Some(RhdCommunication::Auxiliary(checked.0)) + )); + } + } + RhdCommunication::Consensus(RhdLocalized::Propose(p)) => { + if p.sender == self.local_id { continue } + + let checked = check_proposal::( + &self.authorities, + &self.parent_hash, + &p, + ); + + if let Ok(()) = checked { + return Ok(Async::Ready( + Some(RhdCommunication::Consensus(RhdLocalized::Propose(p))) + )); + } + } + RhdCommunication::Consensus(RhdLocalized::Vote(v)) => { + if v.sender == self.local_id { continue } + + let checked = check_vote::( + &self.authorities, + &self.parent_hash, + &v, + ); + + if let Ok(()) = checked { + return Ok(Async::Ready( + Some(RhdCommunication::Consensus(RhdLocalized::Vote(v))) + )); + } + } + } + } + Async::Ready(None) => return Ok(Async::Ready(None)), + Async::NotReady => return Ok(Async::NotReady), + } + } + } +} + /// Given a total number of authorities, yield the maximum faulty that would be allowed. /// This will always be under 1/3. pub fn max_faulty_of(n: usize) -> usize { @@ -634,7 +769,116 @@ pub fn bft_threshold(n: usize) -> usize { n - max_faulty_of(n) } -// /// Sign a BFT message with the given key. +// actions in the signature scheme. +#[derive(Encode)] +enum Action { + Prepare(u32, H), + Commit(u32, H), + AdvanceRound(u32), + // signatures of header hash and full candidate are both included. + ProposeHeader(u32, H), + Propose(u32, B), +} + +// encode something in a way which is localized to a specific parent-hash +fn localized_encode(parent_hash: H, value: E) -> Vec { + (parent_hash, value).encode() +} + +fn check_justification_signed_message( + authorities: &[AuthorityId], + message: &[u8], + just: UncheckedJustification) +-> Result, UncheckedJustification> { + // additional error information could be useful here. + just.0.check(authorities.len() - max_faulty_of(authorities.len()), |_, _, sig| { + let auth_id = sig.signer.clone().into(); + if !authorities.contains(&auth_id) { return None } + + if ed25519::verify_strong(&sig.signature, message, &sig.signer) { + Some(sig.signer.0) + } else { + None + } + }).map(RhdJustification).map_err(UncheckedJustification) +} + +/// Check a full justification for a header hash. +/// Provide all valid authorities. +/// +/// On failure, returns the justification back. +pub fn check_justification( + authorities: &[AuthorityId], + parent: B::Hash, + just: UncheckedJustification +) -> Result, UncheckedJustification> { + let vote: Action = Action::Commit(just.0.round_number as u32, just.0.digest.clone()); + let message = localized_encode(parent, vote); + + check_justification_signed_message(authorities, &message[..], just) +} + +/// Check a prepare justification for a header hash. +/// Provide all valid authorities. +/// +/// On failure, returns the justification back. +pub fn check_prepare_justification(authorities: &[AuthorityId], parent: B::Hash, just: UncheckedJustification) + -> Result, UncheckedJustification> +{ + let vote: Action = Action::Prepare(just.0.round_number as u32, just.0.digest.clone()); + let message = localized_encode(parent, vote); + + check_justification_signed_message(authorities, &message[..], just).map(|e| PrepareJustification(e.0)) +} + +/// Check proposal message signatures and authority. +/// Provide all valid authorities. +pub fn check_proposal( + authorities: &[AuthorityId], + parent_hash: &B::Hash, + propose: &rhododendron::LocalizedProposal) + -> Result<(), Error> +{ + if !authorities.contains(&propose.sender) { + return Err(CommonErrorKind::InvalidAuthority(propose.sender.into()).into()); + } + + let action_header = Action::ProposeHeader(propose.round_number as u32, propose.digest.clone()); + let action_propose = Action::Propose(propose.round_number as u32, propose.proposal.clone()); + check_action::(action_header, parent_hash, &propose.digest_signature)?; + check_action::(action_propose, parent_hash, &propose.full_signature) +} + +/// Check vote message signatures and authority. +/// Provide all valid authorities. +pub fn check_vote( + authorities: &[AuthorityId], + parent_hash: &B::Hash, + vote: &rhododendron::LocalizedVote) + -> Result<(), Error> +{ + if !authorities.contains(&vote.sender) { + return Err(CommonErrorKind::InvalidAuthority(vote.sender.into()).into()); + } + + let action = match vote.vote { + rhododendron::Vote::Prepare(r, ref h) => Action::Prepare(r as u32, h.clone()), + rhododendron::Vote::Commit(r, ref h) => Action::Commit(r as u32, h.clone()), + rhododendron::Vote::AdvanceRound(r) => Action::AdvanceRound(r as u32), + }; + check_action::(action, parent_hash, &vote.signature) +} + +fn check_action(action: Action, parent_hash: &B::Hash, sig: &LocalizedSignature) -> Result<(), Error> { + let message = localized_encode(*parent_hash, action); + if ed25519::verify_strong(&sig.signature, &message, &sig.signer) { + Ok(()) + } else { + Err(CommonErrorKind::InvalidSignature(sig.signature.into(), sig.signer.clone().into()).into()) + } +} + +/// Sign a BFT message with the given key. pub fn sign_message( message: RhdMessage, key: &ed25519::Pair, @@ -642,13 +886,9 @@ pub fn sign_message( ) -> LocalizedMessage { let signer = key.public(); - let sign_action = |action: ::rhododendron::Vote| { - let primitive = ::rhododendron::LocalizedVote { - parent: parent_hash.clone(), - action, - }; + let sign_action = |action: Action| { + let to_sign = localized_encode(parent_hash.clone(), action); - let to_sign = Encode::encode(&primitive); LocalizedSignature { signer: signer.clone(), signature: key.sign(&to_sign), @@ -658,10 +898,10 @@ pub fn sign_message( match message { RhdMessage::Propose(r, proposal) => { let header_hash = proposal.hash(); - let action_header = ::rhododendron::ProposeHeader(r as u32, header_hash.clone()); - let action_propose = ::rhododendron::Propose(r as u32, proposal.clone()); + let action_header = Action::ProposeHeader(r as u32, header_hash.clone()); + let action_propose = Action::Propose(r as u32, proposal.clone()); - ::rhododendron::LocalizedMessage::Propose(::rhododendron::LocalizedProposal { + rhododendron::LocalizedMessage::Propose(rhododendron::LocalizedProposal { round_number: r, proposal, digest: header_hash, @@ -670,20 +910,443 @@ pub fn sign_message( full_signature: sign_action(action_propose), }) } - RhdMessage::Vote(vote) => ::rhododendron::LocalizedMessage::Vote( - ::rhododendron::LocalizedVote { + RhdMessage::Vote(vote) => rhododendron::LocalizedMessage::Vote({ + let action = match vote { + RhdMessageVote::Prepare(r, h) => Action::Prepare(r as u32, h), + RhdMessageVote::Commit(r, h) => Action::Commit(r as u32, h), + RhdMessageVote::AdvanceRound(r) => Action::AdvanceRound(r as u32), + }; + + rhododendron::LocalizedVote { vote: vote, sender: signer.clone().into(), signature: sign_action(action), } - ) + }) } } + +impl<'a, B, E, Block> BlockBuilder for client::block_builder::BlockBuilder<'a, B, E, Block, Blake2Hasher> where + B: client::backend::Backend + Send + Sync + 'static, + E: CallExecutor + Send + Sync + Clone + 'static, + Block: BlockT +{ + fn push_extrinsic(&mut self, extrinsic: ::Extrinsic) -> Result<(), Error> { + client::block_builder::BlockBuilder::push(self, extrinsic).map_err(Into::into) + } +} + +impl<'a, B, E, Block> AuthoringApi for SubstrateClient where + B: client::backend::Backend + Send + Sync + 'static, + E: CallExecutor + Send + Sync + Clone + 'static, + Block: BlockT, +{ + type Block = Block; + type Error = client::error::Error; + + fn build_block) -> ()>( + &self, + at: &BlockId, + inherent_data: InherentData, + mut build_ctx: F, + ) -> Result { + let runtime_version = self.runtime_version_at(at)?; + + let mut block_builder = self.new_block_at(at)?; + if runtime_version.has_api(*b"blkbuild", 1) { + for inherent in self.inherent_extrinsics(at, &inherent_data)? { + block_builder.push(inherent)?; + } + } + + build_ctx(&mut block_builder); + + block_builder.bake().map_err(Into::into) + } +} + + +/// Proposer factory. +pub struct ProposerFactory where + C: AuthoringApi, + A: txpool::ChainApi, +{ + /// The client instance. + pub client: Arc, + /// The transaction pool. + pub transaction_pool: Arc>, + /// The backing network handle. + pub network: N, + /// handle to remote task executor + pub handle: TaskExecutor, + /// Offline-tracker. + pub offline: SharedOfflineTracker, + /// Force delay in evaluation this long. + pub force_delay: u64, +} + +impl consensus::Environment<::Block> for ProposerFactory where + N: Network::Block>, + C: AuthoringApi + BlockNumberToHash, + A: txpool::ChainApi::Block>, + // <::Block as BlockT>::Hash: + // Into<::Hash> + PartialEq + Into, + Error: From<::Error> +{ + type Proposer = Proposer; + type Error = Error; + + fn init( + &self, + parent_header: &<::Block as BlockT>::Header, + authorities: &[AuthorityId], + sign_with: Arc, + ) -> Result { + use runtime_primitives::traits::Hash as HashT; + let parent_hash = parent_header.hash(); + + let id = BlockId::hash(parent_hash); + let random_seed = self.client.random_seed(&id)?; + let random_seed = <<::Block as BlockT>::Header as HeaderT>::Hashing::hash(random_seed.as_ref()); + + let validators = self.client.validators(&id)?; + self.offline.write().note_new_block(&validators[..]); + + info!("Starting consensus session on top of parent {:?}", parent_hash); + + let local_id = sign_with.public().0.into(); + let (input, output) = self.network.communication_for( + authorities, + local_id, + parent_hash.clone(), + self.handle.clone(), + ); + let now = Instant::now(); + let proposer = Proposer { + client: self.client.clone(), + start: now, + local_key: sign_with, + parent_hash, + parent_id: id, + parent_number: *parent_header.number(), + random_seed, + transaction_pool: self.transaction_pool.clone(), + offline: self.offline.clone(), + validators, + minimum_timestamp: current_timestamp() + self.force_delay, + network: self.network.clone() + }; + + Ok(proposer) + } +} + +/// The proposer logic. +pub struct Proposer { + client: Arc, + start: Instant, + local_key: Arc, + parent_hash: <::Block as BlockT>::Hash, + parent_id: BlockId<::Block>, + parent_number: <<::Block as BlockT>::Header as HeaderT>::Number, + random_seed: <::Block as BlockT>::Hash, + transaction_pool: Arc>, + offline: SharedOfflineTracker, + validators: Vec, + minimum_timestamp: u64, + network: N, +} + +impl Proposer { + fn primary_index(&self, round_number: u32, len: usize) -> usize { + use primitives::uint::U256; + + let big_len = U256::from(len); + let offset = U256::from_big_endian(self.random_seed.as_ref()) % big_len; + let offset = offset.low_u64() as usize + round_number as usize; + offset % len + } +} + +impl BaseProposer<::Block> for Proposer where + C: AuthoringApi + BlockNumberToHash, + A: txpool::ChainApi::Block>, + <::Block as BlockT>::Hash: + Into<::Hash> + PartialEq + Into, + error::Error: From<::Error> +{ + type Create = Result<::Block, Error>; + type Error = Error; + type Evaluate = Box>; + + fn propose(&self) -> Self::Create { + use runtime_primitives::traits::BlakeTwo256; + + const MAX_VOTE_OFFLINE_SECONDS: Duration = Duration::from_secs(60); + + let timestamp = ::std::cmp::max(self.minimum_timestamp, current_timestamp()); + + let elapsed_since_start = self.start.elapsed(); + let offline_indices = if elapsed_since_start > MAX_VOTE_OFFLINE_SECONDS { + Vec::new() + } else { + self.offline.read().reports(&self.validators[..]) + }; + + if !offline_indices.is_empty() { + info!( + "Submitting offline validators {:?} for slash-vote", + offline_indices.iter().map(|&i| self.validators[i as usize]).collect::>(), + ) + } + + let inherent_data = InherentData { + timestamp, + offline_indices, + }; + + let block = self.client.build_block( + &self.parent_id, + inherent_data, + |block_builder| { + let mut unqueue_invalid = Vec::new(); + self.transaction_pool.ready(|pending_iterator| { + let mut pending_size = 0; + for pending in pending_iterator { + // TODO [ToDr] Probably get rid of it, and validate in runtime. + let encoded_size = pending.data.encode().len(); + if pending_size + encoded_size >= MAX_TRANSACTIONS_SIZE { break } + + match block_builder.push_extrinsic(pending.data.clone()) { + Ok(()) => { + pending_size += encoded_size; + } + Err(e) => { + trace!(target: "transaction-pool", "Invalid transaction: {}", e); + unqueue_invalid.push(pending.hash.clone()); + } + } + } + }); + + self.transaction_pool.remove_invalid(&unqueue_invalid); + })?; + + info!("Proposing block [number: {}; hash: {}; parent_hash: {}; extrinsics: [{}]]", + block.header().number(), + <::Block as BlockT>::Hash::from(block.header().hash()), + block.header().parent_hash(), + block.extrinsics().iter() + .map(|xt| format!("{}", BlakeTwo256::hash_of(xt))) + .collect::>() + .join(", ") + ); + + let substrate_block = Decode::decode(&mut block.encode().as_slice()) + .expect("blocks are defined to serialize to substrate blocks correctly; qed"); + + assert!(evaluation::evaluate_initial( + &substrate_block, + &self.parent_hash, + self.parent_number, + ).is_ok()); + + Ok(substrate_block) + } + + fn evaluate(&self, unchecked_proposal: &::Block) -> Self::Evaluate { + debug!(target: "rhd", "evaluating block on top of parent ({}, {:?})", self.parent_number, self.parent_hash); + + // do initial serialization and structural integrity checks. + if let Err(e) = evaluation::evaluate_initial( + unchecked_proposal, + &self.parent_hash, + self.parent_number, + ) { + debug!(target: "rhd", "Invalid proposal: {:?}", e); + return Box::new(future::ok(false)); + }; + + let current_timestamp = current_timestamp(); + let inherent = InherentData::new( + current_timestamp, + self.offline.read().reports(&self.validators) + ); + let proposed_timestamp = match self.client.check_inherents( + &self.parent_id, + &unchecked_proposal, + &inherent + ) { + Ok(Ok(())) => None, + Ok(Err(BlockBuilderError::TimestampInFuture(timestamp))) => Some(timestamp), + Ok(Err(e)) => { + debug!(target: "rhd", "Invalid proposal (check_inherents): {:?}", e); + return Box::new(future::ok(false)); + }, + Err(e) => { + debug!(target: "rhd", "Could not call into runtime: {:?}", e); + return Box::new(future::ok(false)); + } + }; + + let vote_delays = { + + // the duration until the given timestamp is current + let proposed_timestamp = ::std::cmp::max(self.minimum_timestamp, proposed_timestamp.unwrap_or(0)); + let timestamp_delay = if proposed_timestamp > current_timestamp { + let delay_s = proposed_timestamp - current_timestamp; + debug!(target: "rhd", "Delaying evaluation of proposal for {} seconds", delay_s); + Some(Instant::now() + Duration::from_secs(delay_s)) + } else { + None + }; + + match timestamp_delay { + Some(duration) => future::Either::A( + Delay::new(duration).map_err(|e| ErrorKind::Timer(e).into()) + ), + None => future::Either::B(future::ok(())), + } + }; + + // evaluate whether the block is actually valid. + // it may be better to delay this until the delays are finished + let evaluated = match self.client.execute_block(&self.parent_id, &unchecked_proposal.clone()) + .map_err(Error::from) { + Ok(()) => Ok(true), + Err(err) => match err.kind() { + error::ErrorKind::Client(client::error::ErrorKind::Execution(_)) => Ok(false), + _ => Err(err) + } + }; + + let future = future::result(evaluated).and_then(move |good| { + let end_result = future::ok(good); + if good { + // delay a "good" vote. + future::Either::A(vote_delays.and_then(|_| end_result)) + } else { + // don't delay a "bad" evaluation. + future::Either::B(end_result) + } + }); + + Box::new(future) as Box<_> + } +} + +impl LocalProposer<::Block> for Proposer where + C: AuthoringApi + BlockNumberToHash, + A: txpool::ChainApi::Block>, + Self: BaseProposer<::Block, Error=Error>, + <::Block as BlockT>::Hash: + Into<::Hash> + PartialEq + Into, + error::Error: From<::Error> +{ + + fn round_proposer(&self, round_number: u32, authorities: &[AuthorityId]) -> AuthorityId { + let offset = self.primary_index(round_number, authorities.len()); + let proposer = authorities[offset as usize].clone(); + trace!(target: "rhd", "proposer for round {} is {}", round_number, proposer); + + proposer + } + + fn import_misbehavior(&self, _misbehavior: Vec<(AuthorityId, Misbehavior<<::Block as BlockT>::Hash>)>) { + use rhododendron::Misbehavior as GenericMisbehavior; + use runtime_primitives::bft::{MisbehaviorKind, MisbehaviorReport}; + use node_runtime::{Call, UncheckedExtrinsic, ConsensusCall}; + + let mut next_index = { + let local_id = self.local_key.public().0; + let cur_index = self.transaction_pool.cull_and_get_pending(&BlockId::hash(self.parent_hash), |pending| pending + .filter(|tx| tx.verified.sender == local_id) + .last() + .map(|tx| Ok(tx.verified.index())) + .unwrap_or_else(|| self.client.account_nonce(&self.parent_id, local_id)) + .map_err(Error::from) + ); + + match cur_index { + Ok(cur_index) => cur_index + 1, + Err(e) => { + warn!(target: "consensus", "Error computing next transaction index: {:?}", e); + return; + } + } + }; + + for (target, misbehavior) in misbehavior { + let report = MisbehaviorReport { + parent_hash: self.parent_hash.into(), + parent_number: self.parent_number.as_(), + target, + misbehavior: match misbehavior { + GenericMisbehavior::ProposeOutOfTurn(_, _, _) => continue, + GenericMisbehavior::DoublePropose(_, _, _) => continue, + GenericMisbehavior::DoublePrepare(round, (h1, s1), (h2, s2)) + => MisbehaviorKind::BftDoublePrepare(round as u32, (h1.into(), s1.signature), (h2.into(), s2.signature)), + GenericMisbehavior::DoubleCommit(round, (h1, s1), (h2, s2)) + => MisbehaviorKind::BftDoubleCommit(round as u32, (h1.into(), s1.signature), (h2.into(), s2.signature)), + } + }; + let payload = ( + next_index, + Call::Consensus(ConsensusCall::report_misbehavior(report)), + Era::immortal(), + self.client.genesis_hash() + ); + let signature = self.local_key.sign(&payload.encode()).into(); + next_index += 1; + + let local_id = self.local_key.public().0.into(); + let extrinsic = UncheckedExtrinsic { + signature: Some((node_runtime::RawAddress::Id(local_id), signature, payload.0, Era::immortal())), + function: payload.1, + }; + let uxt: <::Block as BlockT>::Extrinsic = Decode::decode( + &mut extrinsic.encode().as_slice()).expect("Encoded extrinsic is valid"); + let hash = BlockId::<::Block>::hash(self.parent_hash); + if let Err(e) = self.transaction_pool.submit_one(&hash, uxt) { + warn!("Error importing misbehavior report: {:?}", e); + } + } + } + + fn on_round_end(&self, round_number: u32, was_proposed: bool) { + let primary_validator = self.validators[ + self.primary_index(round_number, self.validators.len()) + ]; + + // alter the message based on whether we think the empty proposer was forced to skip the round. + // this is determined by checking if our local validator would have been forced to skip the round. + if !was_proposed { + let public = ed25519::Public::from_raw(primary_validator.0); + info!( + "Potential Offline Validator: {} failed to propose during assigned slot: {}", + public, + round_number, + ); + } + + self.offline.write().note_round_end(primary_validator, was_proposed); + } +} + +fn current_timestamp() -> u64 { + time::SystemTime::now().duration_since(time::UNIX_EPOCH) + .expect("now always later than unix epoch; qed") + .as_secs() +} + + #[cfg(test)] mod tests { use super::*; use std::collections::HashSet; + use std::marker::PhantomData; + use runtime_primitives::testing::{Block as GenericTestBlock, Header as TestHeader}; use primitives::H256; use self::keyring::Keyring; @@ -698,14 +1361,21 @@ mod tests { } impl BlockImport for FakeClient { - fn import_block(&self, block: TestBlock, _justification: Justification, _authorities: &[AuthorityId]) -> bool { + type Error = Error; + + fn import_block(&self, + block: ImportBlock, + _new_authorities: Option> + ) -> Result { assert!(self.imported_heights.lock().insert(block.header.number)); - true + Ok(ImportResult::Queued) } } impl Authorities for FakeClient { - fn authorities(&self, _at: &BlockId) -> Result, Error> { + type Error = Error; + + fn authorities(&self, _at: &BlockId) -> Result, Self::Error> { Ok(self.authorities.clone()) } } @@ -740,18 +1410,16 @@ mod tests { impl Environment for DummyFactory { type Proposer = DummyProposer; - type Input = Comms; - type Output = Comms; type Error = Error; fn init(&self, parent_header: &TestHeader, _authorities: &[AuthorityId], _sign_with: Arc) - -> Result<(DummyProposer, Self::Input, Self::Output), Error> + -> Result { - Ok((DummyProposer(parent_header.number + 1), Comms(::std::marker::PhantomData), Comms(::std::marker::PhantomData))) + Ok(DummyProposer(parent_header.number + 1)) } } - impl Proposer for DummyProposer { + impl BaseProposer for DummyProposer { type Error = Error; type Create = Result; type Evaluate = Result; @@ -767,11 +1435,13 @@ mod tests { fn evaluate(&self, proposal: &TestBlock) -> Result { Ok(proposal.header.number == self.0) } + } + impl LocalProposer for DummyProposer { fn import_misbehavior(&self, _misbehavior: Vec<(AuthorityId, Misbehavior)>) {} - fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId { - authorities[round_number % authorities.len()].clone() + fn round_proposer(&self, round_number: u32, authorities: &[AuthorityId]) -> AuthorityId { + authorities[(round_number as usize) % authorities.len()].clone() } } @@ -791,9 +1461,9 @@ mod tests { } } - fn sign_vote(vote: ::rhododendron::Vote, key: &ed25519::Pair, parent_hash: H256) -> LocalizedSignature { + fn sign_vote(vote: rhododendron::Vote, key: &ed25519::Pair, parent_hash: H256) -> LocalizedSignature { match sign_message::(vote.into(), key, parent_hash) { - ::rhododendron::LocalizedMessage::Vote(vote) => vote.signature, + rhododendron::LocalizedMessage::Vote(vote) => vote.signature, _ => panic!("signing vote leads to signed vote"), } } @@ -829,10 +1499,10 @@ mod tests { second.parent_hash = first_hash; let _second_hash = second.hash(); - let mut first_bft = service.build_upon(&first).unwrap().unwrap(); + let mut first_bft = service.build_upon(&first, Comms(PhantomData), Comms(PhantomData)).unwrap().unwrap(); assert!(service.live_agreement.lock().as_ref().unwrap().0 == first); - let _second_bft = service.build_upon(&second).unwrap(); + let _second_bft = service.build_upon(&second, Comms(PhantomData), Comms(PhantomData)).unwrap(); assert!(service.live_agreement.lock().as_ref().unwrap().0 != first); assert!(service.live_agreement.lock().as_ref().unwrap().0 == second); @@ -929,8 +1599,8 @@ mod tests { extrinsics: Default::default() }; - let proposal = sign_message(::rhododendron::Message::Propose(1, block.clone()), &Keyring::Alice.pair(), parent_hash);; - if let ::rhododendron::LocalizedMessage::Propose(proposal) = proposal { + let proposal = sign_message(rhododendron::Message::Propose(1, block.clone()), &Keyring::Alice.pair(), parent_hash);; + if let rhododendron::LocalizedMessage::Propose(proposal) = proposal { assert!(check_proposal(&authorities, &parent_hash, &proposal).is_ok()); let mut invalid_round = proposal.clone(); invalid_round.round_number = 0; @@ -943,8 +1613,8 @@ mod tests { } // Not an authority - let proposal = sign_message::(::rhododendron::Message::Propose(1, block), &Keyring::Bob.pair(), parent_hash);; - if let ::rhododendron::LocalizedMessage::Propose(proposal) = proposal { + let proposal = sign_message::(rhododendron::Message::Propose(1, block), &Keyring::Bob.pair(), parent_hash);; + if let rhododendron::LocalizedMessage::Propose(proposal) = proposal { assert!(check_proposal(&authorities, &parent_hash, &proposal).is_err()); } else { assert!(false); @@ -961,8 +1631,8 @@ mod tests { Keyring::Eve.to_raw_public().into(), ]; - let vote = sign_message::(::rhododendron::Message::Vote(::rhododendron::Vote::Prepare(1, hash)), &Keyring::Alice.pair(), parent_hash);; - if let ::rhododendron::LocalizedMessage::Vote(vote) = vote { + let vote = sign_message::(rhododendron::Message::Vote(rhododendron::Vote::Prepare(1, hash)), &Keyring::Alice.pair(), parent_hash);; + if let rhododendron::LocalizedMessage::Vote(vote) = vote { assert!(check_vote::(&authorities, &parent_hash, &vote).is_ok()); let mut invalid_sender = vote.clone(); invalid_sender.signature.signer = Keyring::Eve.into(); @@ -972,8 +1642,8 @@ mod tests { } // Not an authority - let vote = sign_message::(::rhododendron::Message::Vote(::rhododendron::Vote::Prepare(1, hash)), &Keyring::Bob.pair(), parent_hash);; - if let ::rhododendron::LocalizedMessage::Vote(vote) = vote { + let vote = sign_message::(rhododendron::Message::Vote(rhododendron::Vote::Prepare(1, hash)), &Keyring::Bob.pair(), parent_hash);; + if let rhododendron::LocalizedMessage::Vote(vote) = vote { assert!(check_vote::(&authorities, &parent_hash, &vote).is_err()); } else { assert!(false); @@ -1000,7 +1670,7 @@ mod tests { let mut second = from_block_number(3); second.parent_hash = first_hash; - let _ = service.build_upon(&first).unwrap(); + let _ = service.build_upon(&first, Comms(PhantomData), Comms(PhantomData)).unwrap(); assert!(service.live_agreement.lock().as_ref().unwrap().0 == first); service.live_agreement.lock().take(); } @@ -1028,14 +1698,14 @@ mod tests { let mut third = from_block_number(4); third.parent_hash = second.hash(); - let _ = service.build_upon(&first).unwrap(); + let _ = service.build_upon(&first, Comms(PhantomData), Comms(PhantomData)).unwrap(); assert!(service.live_agreement.lock().as_ref().unwrap().0 == first); // BFT has not seen second, but will move forward on third - service.build_upon(&third).unwrap(); + service.build_upon(&third, Comms(PhantomData), Comms(PhantomData)).unwrap(); assert!(service.live_agreement.lock().as_ref().unwrap().0 == third); // but we are not walking backwards - service.build_upon(&second).unwrap(); + service.build_upon(&second, Comms(PhantomData), Comms(PhantomData)).unwrap(); assert!(service.live_agreement.lock().as_ref().unwrap().0 == third); } } diff --git a/substrate/node/consensus/src/service.rs b/substrate/core/consensus/rhd/src/service.rs similarity index 89% rename from substrate/node/consensus/src/service.rs rename to substrate/core/consensus/rhd/src/service.rs index f8ebd6f421..34efc942d4 100644 --- a/substrate/node/consensus/src/service.rs +++ b/substrate/core/consensus/rhd/src/service.rs @@ -22,12 +22,9 @@ use std::thread; use std::time::{Duration, Instant}; use std::sync::Arc; -use rhd::{self, BftService}; use client::{BlockchainEvents, ChainHead, BlockBody}; -use ed25519; use futures::prelude::*; use transaction_pool::txpool::{Pool as TransactionPool, ChainApi as PoolChainApi}; -use primitives; use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, BlockNumberToHash}; use tokio::executor::current_thread::TaskExecutor as LocalThreadHandle; @@ -35,8 +32,11 @@ use tokio::runtime::TaskExecutor as ThreadPoolHandle; use tokio::runtime::current_thread::Runtime as LocalRuntime; use tokio::timer::Interval; +use parking_lot::RwLock; +use consensus::offline_tracker::OfflineTracker; + use super::{Network, ProposerFactory, AuthoringApi}; -use error; +use {consensus, primitives, ed25519, error, BftService, LocalProposer}; const TIMER_DELAY_MS: u64 = 5000; const TIMER_INTERVAL_MS: u64 = 500; @@ -47,11 +47,12 @@ fn start_bft( header: ::Header, bft_service: Arc>, ) where - F: rhd::Environment + 'static, - C: rhd::BlockImport + rhd::Authorities + 'static, + F: consensus::Environment + 'static, + C: consensus::BlockImport + consensus::Authorities + 'static, F::Error: ::std::fmt::Debug, - >::Error: ::std::fmt::Display + Into, - >::Error: ::std::fmt::Display, + >::Error: ::std::fmt::Display + Into, + >::Proposer : LocalProposer, + >::Error: ::std::fmt::Display, Block: BlockT, { let mut handle = LocalThreadHandle::current(); @@ -88,14 +89,12 @@ impl Service { C: BlockchainEvents<::Block> + ChainHead<::Block> + BlockBody<::Block>, - C: bft::BlockImport<::Block> - + bft::Authorities<::Block> + Send + Sync + 'static, + C: consensus::BlockImport<::Block> + + consensus::Authorities<::Block> + Send + Sync + 'static, primitives::H256: From<<::Block as BlockT>::Hash>, <::Block as BlockT>::Hash: PartialEq + PartialEq, N: Network::Block> + Send + 'static, { - use parking_lot::RwLock; - use super::OfflineTracker; let (signal, exit) = ::exit_future::signal(); let thread = thread::spawn(move || { diff --git a/substrate/core/network/Cargo.toml b/substrate/core/network/Cargo.toml index db4f6c34e2..7eadefb363 100644 --- a/substrate/core/network/Cargo.toml +++ b/substrate/core/network/Cargo.toml @@ -17,6 +17,7 @@ linked-hash-map = "0.5" rustc-hex = "1.0" rand = "0.5" substrate-primitives = { path = "../../core/primitives" } +substrate-consensus-common = { path = "../../core/consensus/common" } substrate-client = { path = "../../core/client" } sr-primitives = { path = "../../core/sr-primitives" } parity-codec = "2.1" diff --git a/substrate/core/network/src/chain.rs b/substrate/core/network/src/chain.rs index 97c2322989..74a01577a7 100644 --- a/substrate/core/network/src/chain.rs +++ b/substrate/core/network/src/chain.rs @@ -16,10 +16,12 @@ //! Blockchain access trait -use client::{self, Client as SubstrateClient, ImportBlock, ImportResult, ClientInfo, BlockStatus, CallExecutor}; +use client::{self, Client as SubstrateClient, ClientInfo, BlockStatus, CallExecutor}; use client::error::Error; +use consensus::BlockImport; use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor}; -use runtime_primitives::generic::BlockId; +use runtime_primitives::generic::{BlockId}; +use consensus::{ImportBlock, ImportResult}; use runtime_primitives::Justification; use primitives::{Blake2Hasher, AuthorityId}; @@ -69,9 +71,12 @@ pub trait Client: Send + Sync { impl Client for SubstrateClient where B: client::backend::Backend + Send + Sync + 'static, E: CallExecutor + Send + Sync + 'static, - Block: BlockT, + Self: BlockImport, + Block: BlockT { - fn import(&self, block: ImportBlock, new_authorities: Option>) -> Result { + fn import(&self, block: ImportBlock, new_authorities: Option>) + -> Result + { (self as &SubstrateClient).import_block(block, new_authorities) } diff --git a/substrate/core/network/src/import_queue.rs b/substrate/core/network/src/import_queue.rs index 26ad83e852..955dc12321 100644 --- a/substrate/core/network/src/import_queue.rs +++ b/substrate/core/network/src/import_queue.rs @@ -28,8 +28,6 @@ use std::collections::{HashSet, VecDeque}; use std::sync::{Arc, Weak}; use std::sync::atomic::{AtomicBool, Ordering}; use parking_lot::{Condvar, Mutex, RwLock}; - -pub use client::{BlockOrigin, ImportBlock, ImportResult}; use network_libp2p::{NodeIndex, Severity}; use primitives::AuthorityId; @@ -42,6 +40,9 @@ use protocol::Context; use service::ExecuteInContext; use sync::ChainSync; +pub use consensus::{ImportBlock, ImportResult, BlockOrigin}; + + #[cfg(any(test, feature = "test-helpers"))] use std::cell::RefCell; @@ -65,7 +66,6 @@ pub trait ImportQueue: Send + Sync { /// /// This is called automatically by the network service when synchronization /// begins. - fn start( &self, _sync: Weak>>, @@ -284,7 +284,7 @@ struct SyncLink<'a, B: 'a + BlockT, E: 'a + ExecuteInContext> { } impl<'a, B: 'static + BlockT, E: 'a + ExecuteInContext> SyncLink<'a, B, E> { - /// Execute closure with locked ChainSync. + /// Execute closure with locked ChainSync. fn with_sync, &mut Context)>(&mut self, closure: F) { let service = self.context; let sync = self.chain; @@ -440,7 +440,6 @@ fn import_single_block>( trace!(target: "sync", "Verifying {}({}) failed: {}", number, hash, msg); } BlockImportError::VerificationFailed(peer, msg) - })?; match chain.import(import_block, new_authorities) { @@ -545,7 +544,7 @@ unsafe impl Sync for ImportCB {} #[cfg(any(test, feature = "test-helpers"))] /// A Verifier that accepts all blocks and passes them on with the configured -/// finality to be imported. +/// finality to be imported. pub struct PassThroughVerifier(pub bool); #[cfg(any(test, feature = "test-helpers"))] @@ -564,7 +563,7 @@ impl Verifier for PassThroughVerifier { body, finalized: self.0, external_justification: justification, - internal_justification: vec![], + post_runtime_digests: vec![], auxiliary: Vec::new(), }, None)) } diff --git a/substrate/core/network/src/lib.rs b/substrate/core/network/src/lib.rs index 82bdf0a877..4a441dcc6f 100644 --- a/substrate/core/network/src/lib.rs +++ b/substrate/core/network/src/lib.rs @@ -28,6 +28,7 @@ extern crate substrate_primitives as primitives; extern crate substrate_client as client; extern crate sr_primitives as runtime_primitives; extern crate substrate_network_libp2p as network_libp2p; +extern crate substrate_consensus_common as consensus; extern crate parity_codec as codec; extern crate futures; extern crate rustc_hex; diff --git a/substrate/core/network/src/protocol.rs b/substrate/core/network/src/protocol.rs index a8edaaceb6..4e98ee8f80 100644 --- a/substrate/core/network/src/protocol.rs +++ b/substrate/core/network/src/protocol.rs @@ -776,41 +776,41 @@ macro_rules! construct_simple_protocol { fn on_connect( &mut self, - ctx: &mut $crate::Context<$block>, - who: $crate::NodeIndex, - status: $crate::StatusMessage<$block> + _ctx: &mut $crate::Context<$block>, + _who: $crate::NodeIndex, + _status: $crate::StatusMessage<$block> ) { - $( self.$sub_protocol_name.on_connect(ctx, who, status); )* + $( self.$sub_protocol_name.on_connect(_ctx, _who, _status); )* } - fn on_disconnect(&mut self, ctx: &mut $crate::Context<$block>, who: $crate::NodeIndex) { - $( self.$sub_protocol_name.on_disconnect(ctx, who); )* + fn on_disconnect(&mut self, _ctx: &mut $crate::Context<$block>, _who: $crate::NodeIndex) { + $( self.$sub_protocol_name.on_disconnect(_ctx, _who); )* } fn on_message( &mut self, - ctx: &mut $crate::Context<$block>, - who: $crate::NodeIndex, - message: &mut Option<$crate::message::Message<$block>> + _ctx: &mut $crate::Context<$block>, + _who: $crate::NodeIndex, + _message: &mut Option<$crate::message::Message<$block>> ) { - $( self.$sub_protocol_name.on_message(ctx, who, message); )* + $( self.$sub_protocol_name.on_message(_ctx, _who, _message); )* } fn on_abort(&mut self) { $( self.$sub_protocol_name.on_abort(); )* } - fn maintain_peers(&mut self, ctx: &mut $crate::Context<$block>) { - $( self.$sub_protocol_name.maintain_peers(ctx); )* + fn maintain_peers(&mut self, _ctx: &mut $crate::Context<$block>) { + $( self.$sub_protocol_name.maintain_peers(_ctx); )* } fn on_block_imported( &mut self, - ctx: &mut $crate::Context<$block>, - hash: <$block as $crate::BlockT>::Hash, - header: &<$block as $crate::BlockT>::Header + _ctx: &mut $crate::Context<$block>, + _hash: <$block as $crate::BlockT>::Hash, + _header: &<$block as $crate::BlockT>::Header ) { - $( self.$sub_protocol_name.on_block_imported(ctx, hash, header); )* + $( self.$sub_protocol_name.on_block_imported(_ctx, _hash, _header); )* } } } diff --git a/substrate/core/network/src/service.rs b/substrate/core/network/src/service.rs index c4b10abe7e..0bed8db83d 100644 --- a/substrate/core/network/src/service.rs +++ b/substrate/core/network/src/service.rs @@ -178,6 +178,12 @@ impl, H: ExHashT> Service { } } +impl, H: ExHashT> ::consensus::SyncOracle for Service { + fn is_major_syncing(&self) -> bool { + self.handler.sync().read().status().is_major_syncing() + } +} + impl, H:ExHashT> Drop for Service { fn drop(&mut self) { self.handler.stop(); diff --git a/substrate/core/network/src/specialization.rs b/substrate/core/network/src/specialization.rs index ccd1071adb..70ad9e9b2e 100644 --- a/substrate/core/network/src/specialization.rs +++ b/substrate/core/network/src/specialization.rs @@ -25,9 +25,6 @@ pub trait Specialization: Send + Sync + 'static { /// Get the current specialization-status. fn status(&self) -> Vec; - /// Called on start-up. - fn on_start(&mut self) { } - /// Called when a peer successfully handshakes. fn on_connect(&mut self, ctx: &mut Context, who: NodeIndex, status: ::message::Status); diff --git a/substrate/core/network/src/sync.rs b/substrate/core/network/src/sync.rs index b758b8f205..e69b22fc57 100644 --- a/substrate/core/network/src/sync.rs +++ b/substrate/core/network/src/sync.rs @@ -18,7 +18,8 @@ use std::collections::HashMap; use std::sync::Arc; use protocol::Context; use network_libp2p::{Severity, NodeIndex}; -use client::{BlockStatus, BlockOrigin, ClientInfo}; +use client::{BlockStatus, ClientInfo}; +use consensus::BlockOrigin; use client::error::Error as ClientError; use blocks::{self, BlockCollection}; use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, As, NumberFor}; @@ -77,6 +78,17 @@ pub struct Status { pub best_seen_block: Option>, } +impl Status { + /// Whether the synchronization status is doing major downloading work or + /// is near the head of the chain. + pub fn is_major_syncing(&self) -> bool { + match self.state { + SyncState::Idle => false, + SyncState::Downloading => true, + } + } +} + impl ChainSync { /// Create a new instance. pub(crate) fn new(role: Roles, info: &ClientInfo, import_queue: Arc>) -> Self { diff --git a/substrate/core/network/src/test/mod.rs b/substrate/core/network/src/test/mod.rs index 243e63e2b3..0da6523d0d 100644 --- a/substrate/core/network/src/test/mod.rs +++ b/substrate/core/network/src/test/mod.rs @@ -34,14 +34,16 @@ use service::TransactionPool; use network_libp2p::{NodeIndex, PeerId, Severity}; use keyring::Keyring; use codec::Encode; -use import_queue::{SyncImportQueue, PassThroughVerifier}; -use test_client::{self, TestClient}; +use import_queue::{SyncImportQueue, PassThroughVerifier, Verifier}; +use consensus::BlockOrigin; use specialization::Specialization; use consensus_gossip::ConsensusGossip; use import_queue::ImportQueue; use service::ExecuteInContext; +use test_client; pub use test_client::runtime::{Block, Hash, Transfer, Extrinsic}; +pub use test_client::TestClient; struct DummyContextExecutor(Arc>, Arc>>); unsafe impl Send for DummyContextExecutor {} @@ -135,20 +137,22 @@ pub struct TestPacket { recipient: NodeIndex, } -pub struct Peer { - client: Arc>, +pub type PeersClient = client::Client; + +pub struct Peer> { + client: Arc, pub sync: Arc>, pub queue: Arc>>, - import_queue: Arc>, + import_queue: Arc>, executor: Arc, } -impl Peer { +impl> Peer { fn new( - client: Arc>, + client: Arc, sync: Arc>, queue: Arc>>, - import_queue: Arc>, + import_queue: Arc>, ) -> Self { let executor = Arc::new(DummyContextExecutor(sync.clone(), queue.clone())); Peer { client, sync, queue, import_queue, executor} @@ -201,6 +205,13 @@ impl Peer { self.sync.tick(&mut TestIo::new(&self.queue, None)); } + /// Send block import notifications. + fn send_import_notifications(&self) { + let info = self.client.info().expect("In-mem client does not fail"); + let header = self.client.header(&BlockId::Hash(info.chain.best_hash)).unwrap().unwrap(); + self.sync.on_block_imported(&mut TestIo::new(&self.queue, None), info.chain.best_hash, &header); + } + /// Restart sync for a peer. fn restart_sync(&self) { self.sync.abort(); @@ -218,15 +229,18 @@ impl Peer { } /// Add blocks to the peer -- edit the block before adding - pub fn generate_blocks(&self, count: usize, mut edit_block: F) + pub fn generate_blocks(&self, count: usize, origin: BlockOrigin, 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 {}, (#{}, parent={})", block.header.hash(), block.header.number, block.header.parent_hash); - self.client.justify_and_import(client::BlockOrigin::File, block).unwrap(); + let hash = block.header.hash(); + trace!("Generating {}, (#{}, parent={})", hash, block.header.number, block.header.parent_hash); + let header = block.header.clone(); + self.client.justify_and_import(origin, block).unwrap(); + self.sync.on_block_imported(&mut TestIo::new(&self.queue, None), hash, &header); } } @@ -234,7 +248,7 @@ impl Peer { pub fn push_blocks(&self, count: usize, with_tx: bool) { let mut nonce = 0; if with_tx { - self.generate_blocks(count, |builder| { + self.generate_blocks(count, BlockOrigin::File, |builder| { let transfer = Transfer { from: Keyring::Alice.to_raw_public().into(), to: Keyring::Alice.to_raw_public().into(), @@ -246,7 +260,7 @@ impl Peer { nonce = nonce + 1; }); } else { - self.generate_blocks(count, |_| ()); + self.generate_blocks(count, BlockOrigin::File, |_| ()); } } @@ -258,7 +272,7 @@ impl Peer { } /// Get a reference to the client. - pub fn client(&self) -> &Arc> { + pub fn client(&self) -> &Arc { &self.client } } @@ -277,25 +291,30 @@ impl TransactionPool for EmptyTransactionPool { fn on_broadcasted(&self, _: HashMap>) {} } -pub struct TestNet { - peers: Vec>, - started: bool, - disconnect_events: Vec<(NodeIndex, NodeIndex)>, //disconnected (initiated by, to) -} +pub trait TestNetFactory: Sized { + type Verifier: 'static + Verifier; -impl TestNet { - /// Create new test network with this many peers. - pub fn new(n: usize) -> Self { - Self::new_with_config(n, ProtocolConfig::default()) + /// These two need to be implemented! + fn from_config(config: &ProtocolConfig) -> Self; + fn make_verifier(&self, client: Arc, config: &ProtocolConfig) -> Arc; + + + /// Get reference to peer. + fn peer(&self, i: usize) -> &Peer; + fn peers(&self) -> &Vec>>; + fn mut_peers>>)>(&mut self, closure: F ); + + fn started(&self) -> bool; + fn set_started(&mut self, now: bool); + + fn default_config() -> ProtocolConfig { + ProtocolConfig::default() } - /// 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, - disconnect_events: Vec::new(), - }; + /// Create new test network with this many peers. + fn new(n: usize) -> Self { + let config = Self::default_config(); + let mut net = Self::from_config(&config); for _ in 0..n { net.add_peer(&config); @@ -304,10 +323,11 @@ impl TestNet { } /// Add a peer. - pub fn add_peer(&mut self, config: &ProtocolConfig) { + 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::new(Arc::new(PassThroughVerifier(false)))); + let verifier = self.make_verifier(client.clone(), config); + let import_queue = Arc::new(SyncImportQueue::new(verifier)); let specialization = DummySpecialization { gossip: ConsensusGossip::new(), }; @@ -320,93 +340,107 @@ impl TestNet { specialization ).unwrap(); - self.peers.push(Arc::new(Peer::new( + let peer = Arc::new(Peer::new( client, Arc::new(sync), Arc::new(RwLock::new(VecDeque::new())), import_queue - ))); - } + )); - /// Get reference to peer. - pub fn peer(&self, i: usize) -> &Peer { - &self.peers[i] + self.mut_peers(|peers| { + peers.push(peer.clone()) + }); } /// Start network. fn start(&mut self) { - if self.started { + if self.started() { return; } - for peer in 0..self.peers.len() { - self.peers[peer].start(); - for client in 0..self.peers.len() { - if peer != client { - self.peers[peer].on_connect(client as NodeIndex); + self.mut_peers(|peers| { + for peer in 0..peers.len() { + peers[peer].start(); + for client in 0..peers.len() { + if peer != client { + peers[peer].on_connect(client as NodeIndex); + } } } - } - self.started = true; + }); + self.set_started(true); } /// 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 { - let disconnecting = { - let recipient = packet.recipient; - trace!("--- {} -> {} ---", peer, recipient); - let to_disconnect = self.peers[recipient].receive_message(peer as NodeIndex, packet); - for d in &to_disconnect { - // notify this that disconnecting peers are disconnecting - self.peers[recipient].on_disconnect(*d as NodeIndex); - self.disconnect_events.push((peer, *d)); + fn route(&mut self) { + self.mut_peers(move |peers| { + for peer in 0..peers.len() { + let packet = peers[peer].pending_message(); + if let Some(packet) = packet { + let disconnecting = { + let recipient = packet.recipient; + trace!(target: "sync", "--- {} -> {} ---", peer, recipient); + let to_disconnect = peers[recipient].receive_message(peer as NodeIndex, packet); + for d in &to_disconnect { + // notify this that disconnecting peers are disconnecting + peers[recipient].on_disconnect(*d as NodeIndex); + } + to_disconnect + }; + for d in &disconnecting { + // notify other peers that this peer is disconnecting + peers[*d].on_disconnect(peer as NodeIndex); } - to_disconnect - }; - for d in &disconnecting { - // notify other peers that this peer is disconnecting - self.peers[*d].on_disconnect(peer as NodeIndex); } } - } + }); } /// Route messages between peers until all queues are empty. - pub fn route_until_complete(&mut self) { + fn route_until_complete(&mut self) { while !self.done() { self.route() } } /// Do a step of synchronization. - pub fn sync_step(&mut self) { + fn sync_step(&mut self) { self.route(); - for peer in &mut self.peers { - peer.sync_step(); - } + self.mut_peers(|peers| { + for peer in peers { + peer.sync_step(); + } + }) + } + + /// Send block import notifications for all peers. + fn send_import_notifications(&mut self) { + self.mut_peers(|peers| { + for peer in peers { + peer.send_import_notifications(); + } + }) } /// Restart sync for a peer. - pub fn restart_peer(&mut self, i: usize) { - self.peers[i].restart_sync(); + fn restart_peer(&mut self, i: usize) { + self.peers()[i].restart_sync(); } /// Perform synchronization until complete. - pub fn sync(&mut self) -> u32 { + fn sync(&mut self) -> u32 { self.start(); let mut total_steps = 0; while !self.done() { self.sync_step(); total_steps += 1; + self.route(); } total_steps } /// Do the given amount of sync steps. - pub fn sync_steps(&mut self, count: usize) { + fn sync_steps(&mut self, count: usize) { self.start(); for _ in 0..count { self.sync_step(); @@ -414,7 +448,50 @@ impl TestNet { } /// Whether all peers have synced. - pub fn done(&self) -> bool { - self.peers.iter().all(|p| p.is_done()) + fn done(&self) -> bool { + self.peers().iter().all(|p| p.is_done()) + } +} + +pub struct TestNet { + peers: Vec>>, + started: bool +} + +impl TestNetFactory for TestNet { + type Verifier = PassThroughVerifier; + + /// Create new test network with peers and given config. + fn from_config(_config: &ProtocolConfig) -> Self { + TestNet { + peers: Vec::new(), + started: false + } + } + + fn make_verifier(&self, _client: Arc, _config: &ProtocolConfig) + -> Arc + { + Arc::new(PassThroughVerifier(false)) + } + + fn peer(&self, i: usize) -> &Peer { + &self.peers[i] + } + + fn peers(&self) -> &Vec>> { + &self.peers + } + + fn mut_peers>>)>(&mut self, closure: F ) { + closure(&mut self.peers); + } + + fn started(&self) -> bool { + self.started + } + + fn set_started(&mut self, new: bool) { + self.started = new; } } diff --git a/substrate/core/network/src/test/sync.rs b/substrate/core/network/src/test/sync.rs index 02531259f2..0f9e407828 100644 --- a/substrate/core/network/src/test/sync.rs +++ b/substrate/core/network/src/test/sync.rs @@ -16,6 +16,7 @@ use client::backend::Backend; use client::blockchain::HeaderBackend as BlockchainHeaderBackend; +use consensus::BlockOrigin; use sync::SyncState; use Roles; use super::*; @@ -68,6 +69,7 @@ fn sync_no_common_longer_chain_fails() { fn sync_after_fork_works() { ::env_logger::init().ok(); let mut net = TestNet::new(3); + net.sync_step(); net.peer(0).push_blocks(30, false); net.peer(1).push_blocks(30, false); net.peer(2).push_blocks(30, false); @@ -87,6 +89,20 @@ fn sync_after_fork_works() { assert!(net.peer(2).client.backend().blockchain().canon_equals_to(&peer1_chain)); } +#[test] +fn own_blocks_are_announced() { + ::env_logger::init().ok(); + let mut net = TestNet::new(3); + net.sync(); // connect'em + net.peer(0).generate_blocks(1, BlockOrigin::Own, |_| ()); + net.sync(); + assert_eq!(net.peer(0).client.backend().blockchain().info().unwrap().best_number, 1); + assert_eq!(net.peer(1).client.backend().blockchain().info().unwrap().best_number, 1); + let peer0_chain = net.peer(0).client.backend().blockchain().clone(); + assert!(net.peer(1).client.backend().blockchain().canon_equals_to(&peer0_chain)); + assert!(net.peer(2).client.backend().blockchain().canon_equals_to(&peer0_chain)); +} + #[test] fn blocks_are_not_announced_by_light_nodes() { ::env_logger::init().ok(); diff --git a/substrate/core/primitives/src/ed25519.rs b/substrate/core/primitives/src/ed25519.rs index 2047a0c7af..027298b4b1 100644 --- a/substrate/core/primitives/src/ed25519.rs +++ b/substrate/core/primitives/src/ed25519.rs @@ -31,7 +31,7 @@ pub type Signature = H512; pub const PKCS_LEN: usize = 85; /// A localized signature also contains sender information. -#[derive(PartialEq, Eq, Clone, Debug)] +#[derive(PartialEq, Eq, Clone, Debug, Encode, Decode)] pub struct LocalizedSignature { /// The signer of the signature. pub signer: Public, @@ -40,6 +40,7 @@ pub struct LocalizedSignature { } /// Verify a message without type checking the parameters' types for the right size. +/// Returns true if the signature is good. pub fn verify>(sig: &[u8], message: &[u8], public: P) -> bool { let public_key = untrusted::Input::from(public.as_ref()); let msg = untrusted::Input::from(message); @@ -52,7 +53,7 @@ pub fn verify>(sig: &[u8], message: &[u8], public: P) -> bool { } /// A public key. -#[derive(PartialEq, Eq, Clone)] +#[derive(PartialEq, Eq, Clone, Encode, Decode)] pub struct Public(pub [u8; 32]); /// A key pair. @@ -246,7 +247,7 @@ impl Pair { } } -/// Verify a signature on a message. +/// Verify a signature on a message. Returns true if the signature is good. pub fn verify_strong>(sig: &Signature, message: &[u8], pubkey: P) -> bool { let public_key = untrusted::Input::from(&pubkey.as_ref().0[..]); let msg = untrusted::Input::from(message); diff --git a/substrate/core/rpc/Cargo.toml b/substrate/core/rpc/Cargo.toml index 453b288c9d..3f35394c91 100644 --- a/substrate/core/rpc/Cargo.toml +++ b/substrate/core/rpc/Cargo.toml @@ -22,5 +22,6 @@ tokio = "0.1.7" [dev-dependencies] assert_matches = "1.1" substrate-test-client = { path = "../test-client" } +substrate-consensus-common = { path = "../consensus/common" } rustc-hex = "2.0" hex-literal = "0.1" diff --git a/substrate/core/rpc/src/chain/tests.rs b/substrate/core/rpc/src/chain/tests.rs index fc9817140d..644c7fed07 100644 --- a/substrate/core/rpc/src/chain/tests.rs +++ b/substrate/core/rpc/src/chain/tests.rs @@ -16,9 +16,9 @@ use super::*; use jsonrpc_macros::pubsub; -use client::BlockOrigin; use test_client::{self, TestClient}; use test_client::runtime::{Block, Header}; +use consensus::BlockOrigin; #[test] fn should_return_header() { diff --git a/substrate/core/rpc/src/lib.rs b/substrate/core/rpc/src/lib.rs index 9e4bd96978..dbbc8e3a6a 100644 --- a/substrate/core/rpc/src/lib.rs +++ b/substrate/core/rpc/src/lib.rs @@ -47,6 +47,8 @@ extern crate hex_literal; #[cfg(test)] extern crate substrate_test_client as test_client; #[cfg(test)] +extern crate substrate_consensus_common as consensus; +#[cfg(test)] extern crate rustc_hex; mod errors; diff --git a/substrate/core/rpc/src/state/tests.rs b/substrate/core/rpc/src/state/tests.rs index 74b5fab841..71978f88ea 100644 --- a/substrate/core/rpc/src/state/tests.rs +++ b/substrate/core/rpc/src/state/tests.rs @@ -17,7 +17,7 @@ use super::*; use self::error::{Error, ErrorKind}; -use client::BlockOrigin; +use consensus::BlockOrigin; use jsonrpc_macros::pubsub; use rustc_hex::FromHex; use test_client::{self, runtime, keyring::Keyring, TestClient, BlockBuilderExt}; diff --git a/substrate/core/service/Cargo.toml b/substrate/core/service/Cargo.toml index 694cd5a553..a15e010d5c 100644 --- a/substrate/core/service/Cargo.toml +++ b/substrate/core/service/Cargo.toml @@ -20,6 +20,7 @@ substrate-keystore = { path = "../../core/keystore" } sr-io = { path = "../../core/sr-io" } sr-primitives = { path = "../../core/sr-primitives" } substrate-primitives = { path = "../../core/primitives" } +substrate-consensus-common = { path = "../../core/consensus/common" } substrate-network = { path = "../../core/network" } substrate-client = { path = "../../core/client" } substrate-client-db = { path = "../../core/client/db" } diff --git a/substrate/core/service/src/chain_ops.rs b/substrate/core/service/src/chain_ops.rs index 38aabe7d68..e94db8c6f0 100644 --- a/substrate/core/service/src/chain_ops.rs +++ b/substrate/core/service/src/chain_ops.rs @@ -20,11 +20,12 @@ use std::{self, io::{Read, Write}}; use futures::Future; use serde_json; -use client::BlockOrigin; use runtime_primitives::generic::{SignedBlock, BlockId}; use runtime_primitives::traits::{As, Block, Header}; use network::import_queue::{ImportQueue, BlockData}; use network::message; + +use consensus_common::BlockOrigin; use components::{self, Components, ServiceFactory, FactoryFullConfiguration, FactoryBlockNumber, RuntimeGenesis}; use new_client; use codec::{Decode, Encode}; diff --git a/substrate/core/service/src/components.rs b/substrate/core/service/src/components.rs index a30fb9940e..d2503ded23 100644 --- a/substrate/core/service/src/components.rs +++ b/substrate/core/service/src/components.rs @@ -25,7 +25,7 @@ use chain_spec::ChainSpec; use client_db; use client::{self, Client}; use {error, Service}; -use network::{self, OnDemand}; +use network::{self, OnDemand, import_queue::ImportQueue}; use substrate_executor::{NativeExecutor, NativeExecutionDispatch}; use transaction_pool::txpool::{self, Options as TransactionPoolOptions, Pool as TransactionPool}; use runtime_primitives::{traits::Block as BlockT, traits::Header as HeaderT, BuildStorage}; @@ -136,8 +136,10 @@ pub trait ServiceFactory: 'static + Sized { type FullService: Deref>> + Send + Sync + 'static; /// Extended light service type. type LightService: Deref>> + Send + Sync + 'static; - /// ImportQueue - type ImportQueue: network::import_queue::ImportQueue + 'static; + /// ImportQueue for full client + type FullImportQueue: network::import_queue::ImportQueue + 'static; + /// ImportQueue for light clients + type LightImportQueue: network::import_queue::ImportQueue + 'static; //TODO: replace these with a constructor trait. that TransactionPool implements. /// Extrinsic pool constructor for the full client. @@ -162,7 +164,7 @@ pub trait ServiceFactory: 'static + Sized { fn build_full_import_queue( config: &FactoryFullConfiguration, _client: Arc> - ) -> Result { + ) -> Result { if let Some(name) = config.chain_spec.consensus_engine() { match name { _ => Err(format!("Chain Specification defines unknown consensus engine '{}'", name).into()) @@ -177,7 +179,7 @@ pub trait ServiceFactory: 'static + Sized { fn build_light_import_queue( config: &FactoryFullConfiguration, _client: Arc> - ) -> Result { + ) -> Result { if let Some(name) = config.chain_spec.consensus_engine() { match name { _ => Err(format!("Chain Specification defines unknown consensus engine '{}'", name).into()) @@ -196,13 +198,16 @@ pub trait Components: 'static { /// Client backend. type Backend: 'static + client::backend::Backend, Blake2Hasher>; /// Client executor. - type Executor: 'static + client::CallExecutor, Blake2Hasher> + Send + Sync; + type Executor: 'static + client::CallExecutor, Blake2Hasher> + Send + Sync + Clone; /// Extrinsic pool type. type TransactionPoolApi: 'static + txpool::ChainApi< Hash = <::Block as BlockT>::Hash, Block = FactoryBlock >; + /// Our Import Queue + type ImportQueue: ImportQueue> + 'static; + /// Create client. fn build_client( config: &FactoryFullConfiguration, @@ -221,7 +226,7 @@ pub trait Components: 'static { fn build_import_queue( config: &FactoryFullConfiguration, client: Arc> - ) -> Result<::ImportQueue, error::Error>; + ) -> Result; } /// A struct that implement `Components` for the full client. @@ -234,6 +239,7 @@ impl Components for FullComponents { type Executor = FullExecutor; type Backend = FullBackend; type TransactionPoolApi = ::FullTransactionPoolApi; + type ImportQueue = Factory::FullImportQueue; fn build_client( config: &FactoryFullConfiguration, @@ -267,7 +273,7 @@ impl Components for FullComponents { fn build_import_queue( config: &FactoryFullConfiguration, client: Arc> - ) -> Result<::ImportQueue, error::Error> { + ) -> Result { Factory::build_full_import_queue(config, client) } } @@ -282,6 +288,7 @@ impl Components for LightComponents { type Executor = LightExecutor; type Backend = LightBackend; type TransactionPoolApi = ::LightTransactionPoolApi; + type ImportQueue = ::LightImportQueue; fn build_client( config: &FactoryFullConfiguration, @@ -316,7 +323,7 @@ impl Components for LightComponents { fn build_import_queue( config: &FactoryFullConfiguration, client: Arc> - ) -> Result<::ImportQueue, error::Error> { + ) -> Result { Factory::build_light_import_queue(config, client) } } diff --git a/substrate/core/service/src/consensus.rs b/substrate/core/service/src/consensus.rs new file mode 100644 index 0000000000..61968ee04d --- /dev/null +++ b/substrate/core/service/src/consensus.rs @@ -0,0 +1,263 @@ +// 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 . + +//! provide consensus service to substrate. + +// FIXME: move this into substrate-consensus-common - https://github.com/paritytech/substrate/issues/1021 + +use std::sync::Arc; +use std::time::{self, Duration, Instant}; +use std; + +use client::{self, error, Client as SubstrateClient, CallExecutor}; +use client::runtime_api::{Core, BlockBuilder as BlockBuilderAPI, id::BLOCK_BUILDER}; +use codec::{Decode, Encode}; +use consensus_common::{self, InherentData, evaluation, offline_tracker::OfflineTracker}; +use primitives::{AuthorityId, ed25519, Blake2Hasher}; +use runtime_primitives::traits::{Block as BlockT, Hash as HashT, Header as HeaderT}; +use runtime_primitives::generic::BlockId; +use transaction_pool::txpool::{self, Pool as TransactionPool}; + +use parking_lot::RwLock; + +/// Shared offline validator tracker. +pub type SharedOfflineTracker = Arc>; +type Timestamp = u64; + +// block size limit. +const MAX_TRANSACTIONS_SIZE: usize = 4 * 1024 * 1024; + +/// Build new blocks. +pub trait BlockBuilder { + /// Push an extrinsic onto the block. Fails if the extrinsic is invalid. + fn push_extrinsic(&mut self, extrinsic: ::Extrinsic) -> Result<(), error::Error>; +} + +/// Local client abstraction for the consensus. +pub trait AuthoringApi: + Send + + Sync + + BlockBuilderAPI<::Block, Error=::Error> + + Core<::Block, AuthorityId, Error=::Error> +{ + /// The block used for this API type. + type Block: BlockT; + /// The error used by this API type. + type Error: std::error::Error; + + /// Build a block on top of the given, with inherent extrinsics pre-pushed. + fn build_block) -> ()>( + &self, + at: &BlockId, + inherent_data: InherentData, + build_ctx: F, + ) -> Result; +} + +impl<'a, B, E, Block> BlockBuilder for client::block_builder::BlockBuilder<'a, B, E, Block, Blake2Hasher> where + B: client::backend::Backend + Send + Sync + 'static, + E: CallExecutor + Send + Sync + Clone + 'static, + Block: BlockT +{ + fn push_extrinsic(&mut self, extrinsic: ::Extrinsic) -> Result<(), error::Error> { + client::block_builder::BlockBuilder::push(self, extrinsic).map_err(Into::into) + } +} + +impl<'a, B, E, Block> AuthoringApi for SubstrateClient where + B: client::backend::Backend + Send + Sync + 'static, + E: CallExecutor + Send + Sync + Clone + 'static, + Block: BlockT, +{ + type Block = Block; + type Error = client::error::Error; + + fn build_block) -> ()>( + &self, + at: &BlockId, + inherent_data: InherentData, + mut build_ctx: F, + ) -> Result { + let runtime_version = self.runtime_version_at(at)?; + + let mut block_builder = self.new_block_at(at)?; + if runtime_version.has_api(BLOCK_BUILDER, 1) { + self.inherent_extrinsics(at, &inherent_data)? + .into_iter().try_for_each(|i| block_builder.push(i))?; + } + + build_ctx(&mut block_builder); + + block_builder.bake().map_err(Into::into) + } +} + +/// Proposer factory. +pub struct ProposerFactory where + C: AuthoringApi, + A: txpool::ChainApi, +{ + /// The client instance. + pub client: Arc, + /// The transaction pool. + pub transaction_pool: Arc>, + /// Offline-tracker. + pub offline: SharedOfflineTracker, + /// Force delay in evaluation this long. + pub force_delay: Timestamp, +} + +impl consensus_common::Environment<::Block> for ProposerFactory where + C: AuthoringApi, + A: txpool::ChainApi::Block>, + client::error::Error: From<::Error> +{ + type Proposer = Proposer; + type Error = error::Error; + + fn init( + &self, + parent_header: &<::Block as BlockT>::Header, + _: &[AuthorityId], + _: Arc, + ) -> Result { + let parent_hash = parent_header.hash(); + + let id = BlockId::hash(parent_hash); + + let authorities: Vec = self.client.authorities(&id)?; + self.offline.write().note_new_block(&authorities[..]); + + info!("Starting consensus session on top of parent {:?}", parent_hash); + + let now = Instant::now(); + let proposer = Proposer { + client: self.client.clone(), + start: now, + parent_hash, + parent_id: id, + parent_number: *parent_header.number(), + transaction_pool: self.transaction_pool.clone(), + offline: self.offline.clone(), + authorities, + minimum_timestamp: current_timestamp() + self.force_delay, + }; + + Ok(proposer) + } +} + +/// The proposer logic. +pub struct Proposer { + client: Arc, + start: Instant, + parent_hash: <::Block as BlockT>::Hash, + parent_id: BlockId<::Block>, + parent_number: <<::Block as BlockT>::Header as HeaderT>::Number, + transaction_pool: Arc>, + offline: SharedOfflineTracker, + authorities: Vec, + minimum_timestamp: u64, +} + +impl consensus_common::Proposer<::Block> for Proposer where + C: AuthoringApi, + A: txpool::ChainApi::Block>, + client::error::Error: From<::Error> +{ + type Create = Result<::Block, error::Error>; + type Error = error::Error; + + fn propose(&self) -> Result<::Block, error::Error> { + use runtime_primitives::traits::BlakeTwo256; + + const MAX_VOTE_OFFLINE_SECONDS: Duration = Duration::from_secs(60); + + let timestamp = ::std::cmp::max(self.minimum_timestamp, current_timestamp()); + + let elapsed_since_start = self.start.elapsed(); + let offline_indices = if elapsed_since_start > MAX_VOTE_OFFLINE_SECONDS { + Vec::new() + } else { + self.offline.read().reports(&self.authorities[..]) + }; + + if !offline_indices.is_empty() { + info!("Submitting offline authorities {:?} for slash-vote", + offline_indices.iter().map(|&i| self.authorities[i as usize]).collect::>(), + ) + } + + let inherent_data = InherentData { + timestamp, + offline_indices, + }; + + let block = self.client.build_block( + &self.parent_id, + inherent_data, + |block_builder| { + let mut unqueue_invalid = Vec::new(); + let mut pending_size = 0; + let pending_iterator = self.transaction_pool.ready(); + + for pending in pending_iterator { + // TODO [ToDr] Probably get rid of it, and validate in runtime. + let encoded_size = pending.data.encode().len(); + if pending_size + encoded_size >= MAX_TRANSACTIONS_SIZE { break } + + match block_builder.push_extrinsic(pending.data.clone()) { + Ok(()) => { + pending_size += encoded_size; + } + Err(e) => { + trace!(target: "transaction-pool", "Invalid transaction: {}", e); + unqueue_invalid.push(pending.hash.clone()); + } + } + } + + self.transaction_pool.remove_invalid(&unqueue_invalid); + })?; + + info!("Proposing block [number: {}; hash: {}; parent_hash: {}; extrinsics: [{}]]", + block.header().number(), + <::Block as BlockT>::Hash::from(block.header().hash()), + block.header().parent_hash(), + block.extrinsics().iter() + .map(|xt| format!("{}", BlakeTwo256::hash_of(xt))) + .collect::>() + .join(", ") + ); + + let substrate_block = Decode::decode(&mut block.encode().as_slice()) + .expect("blocks are defined to serialize to substrate blocks correctly; qed"); + + assert!(evaluation::evaluate_initial( + &substrate_block, + &self.parent_hash, + self.parent_number, + ).is_ok()); + + Ok(substrate_block) + } +} + +fn current_timestamp() -> Timestamp { + time::SystemTime::now().duration_since(time::UNIX_EPOCH) + .expect("now always later than unix epoch; qed") + .as_secs() +} diff --git a/substrate/core/service/src/lib.rs b/substrate/core/service/src/lib.rs index 2acc8d7661..9e7c201e8f 100644 --- a/substrate/core/service/src/lib.rs +++ b/substrate/core/service/src/lib.rs @@ -29,6 +29,7 @@ extern crate parking_lot; extern crate substrate_keystore as keystore; extern crate substrate_primitives as primitives; extern crate sr_primitives as runtime_primitives; +extern crate substrate_consensus_common as consensus_common; extern crate substrate_network as network; extern crate substrate_executor; extern crate substrate_client as client; @@ -56,6 +57,7 @@ mod error; mod chain_spec; pub mod config; pub mod chain_ops; +pub mod consensus; use std::io; use std::net::SocketAddr; @@ -63,7 +65,7 @@ use std::collections::HashMap; #[doc(hidden)] pub use std::{ops::Deref, result::Result, sync::Arc}; use futures::prelude::*; -use parking_lot::Mutex; +use parking_lot::{Mutex, RwLock}; use keystore::Store as Keystore; use client::BlockchainEvents; use runtime_primitives::traits::{Header, As}; @@ -80,6 +82,8 @@ pub use chain_spec::ChainSpec; pub use transaction_pool::txpool::{self, Pool as TransactionPool, Options as TransactionPoolOptions, ChainApi, IntoPoolError}; pub use client::ExecutionStrategy; +use consensus_common::offline_tracker::OfflineTracker; +pub use consensus::ProposerFactory; pub use components::{ServiceFactory, FullBackend, FullExecutor, LightBackend, LightExecutor, Components, PoolApi, ComponentClient, ComponentBlock, FullClient, LightClient, FullComponents, LightComponents, @@ -98,6 +102,7 @@ pub struct Service { keystore: Keystore, exit: ::exit_future::Exit, signal: Option, + proposer: Arc, Components::TransactionPoolApi>>, _rpc_http: Option, _rpc_ws: Option>, // WsServer is not `Sync`, but the service needs to be. _telemetry: Option, @@ -118,6 +123,7 @@ pub fn new_client(config: &FactoryFullConfi impl Service where Components: components::Components, + ::Executor: std::clone::Clone, txpool::ExHash: serde::de::DeserializeOwned + serde::Serialize, txpool::ExtrinsicFor: serde::de::DeserializeOwned + serde::Serialize, { @@ -254,6 +260,13 @@ impl Service ) }; + let proposer = Arc::new(ProposerFactory { + client: client.clone(), + transaction_pool: transaction_pool.clone(), + offline: Arc::new(RwLock::new(OfflineTracker::new())), + force_delay: 0 // FIXME: allow this to be configured + }); + // Telemetry let telemetry = match config.telemetry_url { Some(url) => { @@ -287,6 +300,7 @@ impl Service transaction_pool: transaction_pool, signal: Some(signal), keystore: keystore, + proposer, exit, _rpc_http: rpc_http, _rpc_ws: rpc_ws.map(Mutex::new), @@ -303,6 +317,13 @@ impl Service where self.client.clone() } + /// Get shared proposer instance + pub fn proposer(&self) + -> Arc, Components::TransactionPoolApi>> + { + self.proposer.clone() + } + /// Get shared network instance. pub fn network(&self) -> Arc> { self.network.as_ref().expect("self.network always Some").clone() @@ -324,6 +345,7 @@ impl Service where } } + impl Drop for Service where Components: components::Components { fn drop(&mut self) { debug!(target: "service", "Substrate service shutdown"); @@ -450,7 +472,7 @@ macro_rules! construct_simple_service { $name: ident ) => { pub struct $name { - inner: $crate::Service, + inner: $crate::Arc<$crate::Service>, } impl $name { @@ -460,7 +482,7 @@ macro_rules! construct_simple_service { ) -> $crate::Result { Ok( Self { - inner: $crate::Service::new(config, executor)? + inner: $crate::Arc::new($crate::Service::new(config, executor)?) } ) } @@ -525,8 +547,9 @@ macro_rules! construct_service_factory { Configuration = $config:ty, FullService = $full_service:ty { $( $full_service_init:tt )* }, LightService = $light_service:ty { $( $light_service_init:tt )* }, - ImportQueue = $import_queue:ty - { $( $full_import_queue_init:tt )* } + FullImportQueue = $full_import_queue:ty + { $( $full_import_queue_init:tt )* }, + LightImportQueue = $light_import_queue:ty { $( $light_import_queue_init:tt )* }, } ) => { @@ -544,7 +567,8 @@ macro_rules! construct_service_factory { type Configuration = $config; type FullService = $full_service; type LightService = $light_service; - type ImportQueue = $import_queue; + type FullImportQueue = $full_import_queue; + type LightImportQueue = $light_import_queue; fn build_full_transaction_pool( config: $crate::TransactionPoolOptions, @@ -571,14 +595,14 @@ macro_rules! construct_service_factory { fn build_full_import_queue( config: &$crate::FactoryFullConfiguration, client: $crate::Arc<$crate::FullClient>, - ) -> $crate::Result { + ) -> $crate::Result { ( $( $full_import_queue_init )* ) (config, client) } fn build_light_import_queue( config: &FactoryFullConfiguration, client: Arc<$crate::LightClient>, - ) -> Result { + ) -> Result { ( $( $light_import_queue_init )* ) (config, client) } diff --git a/substrate/core/service/test/Cargo.toml b/substrate/core/service/test/Cargo.toml index cfdfacf4da..12282b16f1 100644 --- a/substrate/core/service/test/Cargo.toml +++ b/substrate/core/service/test/Cargo.toml @@ -12,6 +12,7 @@ env_logger = "0.4" fdlimit = "0.1" substrate-service = { path = "../../../core/service" } substrate-network = { path = "../../../core/network" } +substrate-consensus-common = { path = "../../../core/consensus/common" } substrate-primitives = { path = "../../../core/primitives" } substrate-client = { path = "../../../core/client" } sr-primitives = { path = "../../../core/sr-primitives" } diff --git a/substrate/core/service/test/src/lib.rs b/substrate/core/service/test/src/lib.rs index 31ac007c4f..0a967e3cd6 100644 --- a/substrate/core/service/test/src/lib.rs +++ b/substrate/core/service/test/src/lib.rs @@ -27,6 +27,7 @@ extern crate substrate_service as service; extern crate substrate_network as network; extern crate substrate_primitives as primitives; extern crate substrate_client as client; +extern crate substrate_consensus_common as consensus; extern crate sr_primitives; use std::iter; use std::sync::Arc; @@ -47,9 +48,9 @@ use service::{ FactoryExtrinsic, }; use network::{NetworkConfiguration, NonReservedPeerMode, Protocol, SyncProvider, ManageNetwork}; -use client::ImportBlock; use sr_primitives::traits::As; use sr_primitives::generic::BlockId; +use consensus::{ImportBlock, BlockImport}; struct TestNet { runtime: Runtime, diff --git a/substrate/core/sr-api/src/lib.rs b/substrate/core/sr-api/src/lib.rs index 0ddc89b14e..7fffc43590 100644 --- a/substrate/core/sr-api/src/lib.rs +++ b/substrate/core/sr-api/src/lib.rs @@ -28,7 +28,7 @@ extern crate sr_version as runtime_version; #[doc(hidden)] pub use primitives::{traits::Block as BlockT, generic::BlockId, transaction_validity::TransactionValidity, ApplyResult}; -use runtime_version::RuntimeVersion; +use runtime_version::{ApiId, RuntimeVersion}; use rstd::vec::Vec; #[doc(hidden)] pub use rstd::slice; @@ -429,6 +429,20 @@ macro_rules! decl_apis { }; } +/// The ApiIds for the various standard runtime APIs. +pub mod id { + use super::ApiId; + + /// ApiId for the BlockBuilder trait. + pub const BLOCK_BUILDER: ApiId = *b"blkbuild"; + + /// ApiId for the TaggedTransactionQueue trait. + pub const TAGGED_TRANSACTION_QUEUE: ApiId = *b"validatx"; + + /// ApiId for the Metadata trait. + pub const METADATA: ApiId = *b"metadata"; +} + decl_apis! { /// The `Core` api trait that is mandantory for each runtime. pub trait Core { @@ -453,13 +467,6 @@ decl_apis! { fn validate_transaction(tx: ::Extrinsic) -> TransactionValidity; } - /// The `Miscellaneous` api trait for getting miscellaneous information from the runtime. - pub trait Miscellaneous { - fn validator_count() -> u32; - fn validators() -> Vec; - fn timestamp() -> Moment; - } - /// The `BlockBuilder` api trait that provides required functions for building a block for a runtime. pub trait BlockBuilder ExtraClientSide { /// Initialise a block with the given header. diff --git a/substrate/core/sr-primitives/Cargo.toml b/substrate/core/sr-primitives/Cargo.toml index df80d43a9f..45e0396df3 100644 --- a/substrate/core/sr-primitives/Cargo.toml +++ b/substrate/core/sr-primitives/Cargo.toml @@ -31,3 +31,4 @@ std = [ "substrate-primitives/std", ] api-for-runtime = [] + diff --git a/substrate/core/sr-primitives/src/generic/digest.rs b/substrate/core/sr-primitives/src/generic/digest.rs index 4d44ff5ce0..41ffd6da0d 100644 --- a/substrate/core/sr-primitives/src/generic/digest.rs +++ b/substrate/core/sr-primitives/src/generic/digest.rs @@ -21,6 +21,8 @@ use rstd::prelude::*; use codec::{Decode, Encode, Codec, Input}; use traits::{self, Member, DigestItem as DigestItemT}; +use substrate_primitives::hash::H512 as Signature; + #[derive(PartialEq, Eq, Clone, Encode, Decode)] #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] pub struct Digest { @@ -46,6 +48,10 @@ impl traits::Digest for Digest where fn push(&mut self, item: Self::Item) { self.logs.push(item); } + + fn pop(&mut self) -> Option { + self.logs.pop() + } } /// Digest item that is able to encode/decode 'system' digest items and @@ -60,10 +66,13 @@ pub enum DigestItem { /// block. It is created for every block iff runtime supports changes /// trie creation. ChangesTrieRoot(Hash), + /// Put a Seal on it + Seal(u64, Signature), /// Any 'non-system' digest item, opaque to the native code. Other(Vec), } + /// A 'referencing view' for digest item. Does not own its contents. Used by /// final runtime implementations for encoding/decoding its log items. #[derive(PartialEq, Eq, Clone)] @@ -73,6 +82,9 @@ pub enum DigestItemRef<'a, Hash: 'a, AuthorityId: 'a> { AuthoritiesChange(&'a [AuthorityId]), /// Reference to `DigestItem::ChangesTrieRoot`. ChangesTrieRoot(&'a Hash), + /// A sealed signature for testing + Seal(&'a u64, &'a Signature), + /// Any 'non-system' digest item, opaque to the native code. /// Reference to `DigestItem::Other`. Other(&'a Vec), } @@ -87,6 +99,7 @@ enum DigestItemType { Other = 0, AuthoritiesChange, ChangesTrieRoot, + Seal, } impl DigestItem { @@ -103,6 +116,7 @@ impl DigestItem { match *self { DigestItem::AuthoritiesChange(ref v) => DigestItemRef::AuthoritiesChange(v), DigestItem::ChangesTrieRoot(ref v) => DigestItemRef::ChangesTrieRoot(v), + DigestItem::Seal(ref v, ref s) => DigestItemRef::Seal(v, s), DigestItem::Other(ref v) => DigestItemRef::Other(v), } } @@ -137,6 +151,10 @@ impl Decode for DigestItem DigestItemType::ChangesTrieRoot => Some(DigestItem::ChangesTrieRoot( Decode::decode(input)?, )), + DigestItemType::Seal => { + let vals: (u64, Signature) = Decode::decode(input)?; + Some(DigestItem::Seal(vals.0, vals.1)) + }, DigestItemType::Other => Some(DigestItem::Other( Decode::decode(input)?, )), @@ -173,6 +191,10 @@ impl<'a, Hash: Encode, AuthorityId: Encode> Encode for DigestItemRef<'a, Hash, A DigestItemType::ChangesTrieRoot.encode_to(&mut v); changes_trie_root.encode_to(&mut v); }, + DigestItemRef::Seal(val, sig) => { + DigestItemType::Seal.encode_to(&mut v); + (val, sig).encode_to(&mut v); + }, DigestItemRef::Other(val) => { DigestItemType::Other.encode_to(&mut v); val.encode_to(&mut v); diff --git a/substrate/core/sr-primitives/src/generic/header.rs b/substrate/core/sr-primitives/src/generic/header.rs index ff8222c59a..fc6f73b5cf 100644 --- a/substrate/core/sr-primitives/src/generic/header.rs +++ b/substrate/core/sr-primitives/src/generic/header.rs @@ -137,6 +137,7 @@ impl traits::Header for Header &Self::Digest { &self.digest } + fn digest_mut(&mut self) -> &mut Self::Digest { &mut self.digest } fn set_digest(&mut self, digest: Self::Digest) { self.digest = digest } fn new( diff --git a/substrate/core/sr-primitives/src/testing.rs b/substrate/core/sr-primitives/src/testing.rs index ab57f1aa6c..847af9a3c2 100644 --- a/substrate/core/sr-primitives/src/testing.rs +++ b/substrate/core/sr-primitives/src/testing.rs @@ -42,6 +42,10 @@ impl traits::Digest for Digest { fn push(&mut self, item: Self::Item) { self.logs.push(item); } + + fn pop(&mut self) -> Option { + self.logs.pop() + } } #[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug, Encode, Decode)] @@ -74,6 +78,7 @@ impl traits::Header for Header { fn set_parent_hash(&mut self, hash: Self::Hash) { self.parent_hash = hash } fn digest(&self) -> &Self::Digest { &self.digest } + fn digest_mut(&mut self) -> &mut Self::Digest { &mut self.digest } fn set_digest(&mut self, digest: Self::Digest) { self.digest = digest } fn new( diff --git a/substrate/core/sr-primitives/src/traits.rs b/substrate/core/sr-primitives/src/traits.rs index 4fc09f3e9e..0ee2db117a 100644 --- a/substrate/core/sr-primitives/src/traits.rs +++ b/substrate/core/sr-primitives/src/traits.rs @@ -197,11 +197,13 @@ impl Clear for T { pub trait SimpleBitOps: Sized + Clear + rstd::ops::BitOr + + rstd::ops::BitXor + rstd::ops::BitAnd {} impl + + rstd::ops::BitXor + rstd::ops::BitAnd > SimpleBitOps for T {} @@ -429,6 +431,8 @@ pub trait Header: Clone + Send + Sync + Codec + Eq + MaybeSerializeDebug + 'stat fn set_parent_hash(&mut self, Self::Hash); fn digest(&self) -> &Self::Digest; + /// Get a mutable reference to the digest. + fn digest_mut(&mut self) -> &mut Self::Digest; fn set_digest(&mut self, Self::Digest); fn hash(&self) -> Self::Hash { @@ -458,6 +462,10 @@ pub trait Block: Clone + Send + Sync + Codec + Eq + MaybeSerializeDebug + 'stati pub type HashFor = <::Header as Header>::Hashing; /// Extract the number type for a block. pub type NumberFor = <::Header as Header>::Number; +/// Extract the digest type for a block. +pub type DigestFor = <::Header as Header>::Digest; +/// Extract the digest item type for a block. +pub type DigestItemFor = as Digest>::Item; /// A "checkable" piece of information, used by the standard Substrate Executive in order to /// check the validity of a piece of extrinsic information, usually by verifying the signature. @@ -516,6 +524,8 @@ pub trait Digest: Member + Default { fn logs(&self) -> &[Self::Item]; /// Push new digest item. fn push(&mut self, item: Self::Item); + /// Pop a digest item. + fn pop(&mut self) -> Option; /// Get reference to the first digest item that matches the passed predicate. fn log Option<&T>>(&self, predicate: F) -> Option<&T> { diff --git a/substrate/core/state-machine/src/lib.rs b/substrate/core/state-machine/src/lib.rs index 4853b2afb5..9bb9110886 100644 --- a/substrate/core/state-machine/src/lib.rs +++ b/substrate/core/state-machine/src/lib.rs @@ -64,6 +64,9 @@ pub use overlayed_changes::OverlayedChanges; pub use trie_backend_essence::Storage; pub use trie_backend::TrieBackend; +/// Default num of pages for the heap +const DEFAULT_HEAP_PAGES :u64 = 1024; + /// State Machine Error bound. /// /// This should reflect WASM error type bound for future compatibility. @@ -291,7 +294,7 @@ where .to_vec(); let heap_pages = try_read_overlay_value(overlay, backend, well_known_keys::HEAP_PAGES)? - .and_then(|v| u64::decode(&mut &v[..])).unwrap_or(8) as usize; + .and_then(|v| u64::decode(&mut &v[..])).unwrap_or(DEFAULT_HEAP_PAGES) as usize; // read changes trie configuration. The reason why we're doing it here instead of the // `OverlayedChanges` constructor is that we need proofs for this read as a part of diff --git a/substrate/core/test-client/Cargo.toml b/substrate/core/test-client/Cargo.toml index a1bf2f592e..aa48f95211 100644 --- a/substrate/core/test-client/Cargo.toml +++ b/substrate/core/test-client/Cargo.toml @@ -7,6 +7,7 @@ authors = ["Parity Technologies "] substrate-client = { path = "../client" } parity-codec = "2.1" substrate-executor = { path = "../executor" } +substrate-consensus-common = { path = "../consensus/common" } substrate-keyring = { path = "../../core/keyring" } substrate-primitives = { path = "../primitives" } substrate-test-runtime = { path = "../test-runtime" } diff --git a/substrate/core/test-client/src/client_ext.rs b/substrate/core/test-client/src/client_ext.rs index 76e9888d0b..61133c4d9d 100644 --- a/substrate/core/test-client/src/client_ext.rs +++ b/substrate/core/test-client/src/client_ext.rs @@ -16,15 +16,17 @@ //! Client extension for tests. -use client::{self, ImportBlock, Client}; +use client::{self, Client}; +use consensus::{ImportBlock, BlockImport, BlockOrigin}; use runtime_primitives::generic::BlockId; use primitives::Blake2Hasher; use runtime; /// Extension trait for a test client. -pub trait TestClient { +pub trait TestClient: Sized { /// Justify and import block to the chain. No finality. - fn justify_and_import(&self, origin: client::BlockOrigin, block: runtime::Block) -> client::error::Result<()>; + fn justify_and_import(&self, origin: BlockOrigin, block: runtime::Block) + -> client::error::Result<()>; /// Finalize a block. fn finalize_block(&self, id: BlockId) -> client::error::Result<()>; @@ -36,21 +38,23 @@ pub trait TestClient { impl TestClient for Client where B: client::backend::Backend, - E: client::CallExecutor + E: client::CallExecutor, + Self: BlockImport { - fn justify_and_import(&self, origin: client::BlockOrigin, block: runtime::Block) -> client::error::Result<()> { + fn justify_and_import(&self, origin: BlockOrigin, block: runtime::Block) + -> client::error::Result<()> + { let import = ImportBlock { origin, header: block.header, external_justification: vec![], - internal_justification: vec![], + post_runtime_digests: vec![], body: Some(block.extrinsics), finalized: false, auxiliary: Vec::new(), }; - self.import_block(import, None)?; - Ok(()) + self.import_block(import, None).map(|_| ()) } fn finalize_block(&self, id: BlockId) -> client::error::Result<()> { diff --git a/substrate/core/test-client/src/lib.rs b/substrate/core/test-client/src/lib.rs index b692d4858b..ff3cfbac0e 100644 --- a/substrate/core/test-client/src/lib.rs +++ b/substrate/core/test-client/src/lib.rs @@ -28,6 +28,7 @@ extern crate sr_primitives as runtime_primitives; pub extern crate substrate_client as client; pub extern crate substrate_keyring as keyring; pub extern crate substrate_test_runtime as runtime; +pub extern crate substrate_consensus_common as consensus; pub mod client_ext; pub mod trait_tests; diff --git a/substrate/core/test-client/src/trait_tests.rs b/substrate/core/test-client/src/trait_tests.rs index 86d08cd8a5..3a05b78600 100644 --- a/substrate/core/test-client/src/trait_tests.rs +++ b/substrate/core/test-client/src/trait_tests.rs @@ -21,7 +21,7 @@ use std::sync::Arc; use keyring::Keyring; -use client::BlockOrigin; +use consensus::BlockOrigin; use primitives::Blake2Hasher; use ::TestClient; use runtime_primitives::traits::Block as BlockT; diff --git a/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm b/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm new file mode 100644 index 0000000000..5d9b1fcac3 Binary files /dev/null and b/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm differ diff --git a/substrate/node/cli/Cargo.toml b/substrate/node/cli/Cargo.toml index 61b31a5f67..6f1f92cc82 100644 --- a/substrate/node/cli/Cargo.toml +++ b/substrate/node/cli/Cargo.toml @@ -9,14 +9,19 @@ log = "0.4" tokio = "0.1.7" exit-future = "0.1" substrate-cli = { path = "../../core/cli" } +parity-codec = { version = "2.1" } +parking_lot = "0.4" +slog = "^2" +sr-io = { path = "../../core/sr-io" } +substrate-client = { path = "../../core/client" } substrate-primitives = { path = "../../core/primitives" } node-runtime = { path = "../runtime" } node-primitives = { path = "../primitives" } -node-network = { path = "../network" } hex-literal = "0.1" substrate-service = { path = "../../core/service" } substrate-transaction-pool = { path = "../../core/transaction-pool" } substrate-network = { path = "../../core/network" } +substrate-consensus-aura = { path = "../../core/consensus/aura" } sr-primitives = { path = "../../core/sr-primitives" } node-executor = { path = "../executor" } diff --git a/substrate/node/cli/src/chain_spec.rs b/substrate/node/cli/src/chain_spec.rs index cc4612f27b..53cf074767 100644 --- a/substrate/node/cli/src/chain_spec.rs +++ b/substrate/node/cli/src/chain_spec.rs @@ -17,8 +17,9 @@ //! Substrate chain configurations. use primitives::{AuthorityId, ed25519}; +use node_primitives::AccountId; use node_runtime::{GenesisConfig, ConsensusConfig, CouncilSeatsConfig, CouncilVotingConfig, DemocracyConfig, - SessionConfig, StakingConfig, TimestampConfig, BalancesConfig, TreasuryConfig, + SessionConfig, StakingConfig, TimestampConfig, BalancesConfig, TreasuryConfig, UpgradeKeyConfig, ContractConfig, Permill, Perbill}; use substrate_service; @@ -45,7 +46,7 @@ fn staging_testnet_config_genesis() -> GenesisConfig { const CENTS: u128 = 1_000 * MILLICENTS; // assume this is worth about a cent. const DOLLARS: u128 = 100 * CENTS; - const SECS_PER_BLOCK: u64 = 5; + const SECS_PER_BLOCK: u64 = 4; const MINUTES: u64 = 60 / SECS_PER_BLOCK; const HOURS: u64 = MINUTES * 60; const DAYS: u64 = HOURS * 24; @@ -121,6 +122,9 @@ fn staging_testnet_config_genesis() -> GenesisConfig { max_depth: 1024, block_gas_limit: 10_000_000, }), + upgrade_key: Some(UpgradeKeyConfig { + key: endowed_accounts[0].clone(), + }), } } @@ -139,7 +143,7 @@ pub fn staging_testnet_config() -> ChainSpec { ) } -fn testnet_genesis(initial_authorities: Vec) -> GenesisConfig { +fn testnet_genesis(initial_authorities: Vec, upgrade_key: AccountId) -> GenesisConfig { let endowed_accounts = vec![ ed25519::Pair::from_seed(b"Alice ").public().0.into(), ed25519::Pair::from_seed(b"Bob ").public().0.into(), @@ -220,13 +224,18 @@ fn testnet_genesis(initial_authorities: Vec) -> GenesisConfig { max_depth: 1024, block_gas_limit: 10_000_000, }), + upgrade_key: Some(UpgradeKeyConfig { + key: upgrade_key, + }), } } fn development_config_genesis() -> GenesisConfig { testnet_genesis(vec![ ed25519::Pair::from_seed(b"Alice ").public().into(), - ]) + ], + ed25519::Pair::from_seed(b"Alice ").public().0.into() + ) } /// Development config (single validator Alice) @@ -238,7 +247,9 @@ fn local_testnet_genesis() -> GenesisConfig { testnet_genesis(vec![ ed25519::Pair::from_seed(b"Alice ").public().into(), ed25519::Pair::from_seed(b"Bob ").public().into(), - ]) + ], + ed25519::Pair::from_seed(b"Alice ").public().0.into() + ) } /// Local testnet config (multivalidator Alice + Bob) diff --git a/substrate/node/cli/src/lib.rs b/substrate/node/cli/src/lib.rs index 0d8c9c4d71..beb47f91ff 100644 --- a/substrate/node/cli/src/lib.rs +++ b/substrate/node/cli/src/lib.rs @@ -30,9 +30,9 @@ extern crate hex_literal; #[cfg(test)] extern crate substrate_service_test as service_test; extern crate substrate_transaction_pool as transaction_pool; +#[macro_use] extern crate substrate_network as network; -extern crate node_network; -extern crate sr_primitives as runtime_primitives; +extern crate substrate_consensus_aura as consensus; extern crate node_primitives; #[macro_use] extern crate substrate_service; diff --git a/substrate/node/cli/src/service.rs b/substrate/node/cli/src/service.rs index 4e718816e1..d02ea48767 100644 --- a/substrate/node/cli/src/service.rs +++ b/substrate/node/cli/src/service.rs @@ -22,38 +22,19 @@ use std::sync::Arc; use transaction_pool::{self, txpool::{Pool as TransactionPool}}; use node_primitives::Block; use node_runtime::GenesisConfig; -use node_network::Protocol as NodeProtocol; use substrate_service::{ FactoryFullConfiguration, LightComponents, FullComponents, FullBackend, - LightBackend, FullExecutor, LightExecutor + FullClient, LightClient, LightBackend, FullExecutor, LightExecutor, + Roles, TaskExecutor, }; -use network::import_queue::{BasicQueue, BlockOrigin, ImportBlock, Verifier}; -use runtime_primitives::{traits::Block as BlockT}; -use primitives::AuthorityId; use node_executor; +use consensus::{import_queue, start_aura, Config as AuraConfig, AuraImportQueue}; -// TODO: Remove me, when we have a functional consensus. -/// A verifier that doesn't actually do any checks -pub struct NoneVerifier; -/// This Verifiyer accepts all data as valid -impl Verifier for NoneVerifier { - fn verify( - &self, - origin: BlockOrigin, - header: B::Header, - justification: Vec, - body: Option> - ) -> Result<(ImportBlock, Option>), String> { - Ok((ImportBlock { - origin, - header, - body, - finalized: true, - external_justification: justification, - internal_justification: vec![], - auxiliary: Vec::new(), - }, None)) - } +const AURA_SLOT_DURATION: u64 = 6; + +construct_simple_protocol! { + /// Demo protocol attachment for substrate. + pub struct NodeProtocol where Block = Block { } } construct_simple_service!(Service); @@ -70,15 +51,48 @@ construct_service_factory! { Genesis = GenesisConfig, Configuration = (), FullService = Service> - { |config, executor| Service::>::new(config, executor) }, + { |config: FactoryFullConfiguration, executor: TaskExecutor| { + let is_auth = config.roles == Roles::AUTHORITY; + Service::>::new(config, executor.clone()).map(move |service|{ + if is_auth { + if let Ok(Some(Ok(key))) = service.keystore().contents() + .map(|keys| keys.get(0).map(|k| service.keystore().load(k, ""))) + { + info!("Using authority key {}", key.public()); + let task = start_aura( + AuraConfig { + local_key: Some(Arc::new(key)), + slot_duration: AURA_SLOT_DURATION, + }, + service.client(), + service.proposer(), + service.network(), + ); + + executor.spawn(task); + } + } + + service + }) + } + }, LightService = Service> { |config, executor| Service::>::new(config, executor) }, - ImportQueue = BasicQueue - { |_, _| Ok(BasicQueue::new(Arc::new(NoneVerifier {}))) } - { |_, _| Ok(BasicQueue::new(Arc::new(NoneVerifier {}))) }, + FullImportQueue = AuraImportQueue> + { |config, client| Ok(import_queue(AuraConfig { + local_key: None, + slot_duration: 5 + }, client)) }, + LightImportQueue = AuraImportQueue> + { |config, client| Ok(import_queue(AuraConfig { + local_key: None, + slot_duration: 5 + }, client)) }, } } + #[cfg(test)] mod tests { #[cfg(feature = "rhd")] diff --git a/substrate/node/consensus/Cargo.toml b/substrate/node/consensus/Cargo.toml deleted file mode 100644 index c45818ee07..0000000000 --- a/substrate/node/consensus/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "node-consensus" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -error-chain = "0.12" -exit-future = "0.1" -futures = "0.1.17" -log = "0.4" -node-primitives = { path = "../primitives" } -node-runtime = { path = "../runtime" } -parity-codec = "2.1" -parking_lot = "0.4" -rhododendron = "0.3" -sr-primitives = { path = "../../core/sr-primitives" } -srml-system = { path = "../../srml/system" } -substrate-client = { path = "../../core/client" } -substrate-primitives = { path = "../../core/primitives" } -substrate-transaction-pool = { path = "../../core/transaction-pool" } -tokio = "0.1.7" - -[dev-dependencies] -substrate-keyring = { path = "../../core/keyring" } diff --git a/substrate/node/consensus/README.adoc b/substrate/node/consensus/README.adoc deleted file mode 100644 index ca2daa9eb2..0000000000 --- a/substrate/node/consensus/README.adoc +++ /dev/null @@ -1,5 +0,0 @@ - -= Substrate Consensus - -placeholder -//TODO Write content :) diff --git a/substrate/node/consensus/src/error.rs b/substrate/node/consensus/src/error.rs deleted file mode 100644 index 13192ae202..0000000000 --- a/substrate/node/consensus/src/error.rs +++ /dev/null @@ -1,52 +0,0 @@ -// 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 . - -//! Errors that can occur during the consensus process. - -use primitives::AuthorityId; -use client; - -error_chain! { - links { - Client(client::error::Error, client::error::ErrorKind); - Bft(::bft::Error, ::bft::ErrorKind); - } - - errors { - NotValidator(id: AuthorityId) { - description("Local account ID not a validator at this block."), - display("Local account ID ({:?}) not a validator at this block.", id), - } - PrematureDestruction { - description("Proposer destroyed before finishing proposing or evaluating"), - display("Proposer destroyed before finishing proposing or evaluating"), - } - Timer(e: ::tokio::timer::Error) { - description("Failed to register or resolve async timer."), - display("Timer failed: {}", e), - } - Executor(e: ::futures::future::ExecuteErrorKind) { - description("Unable to dispatch agreement future"), - display("Unable to dispatch agreement future: {:?}", e), - } - } -} - -impl From<::bft::InputStreamConcluded> for Error { - fn from(err: ::bft::InputStreamConcluded) -> Self { - ::bft::Error::from(err).into() - } -} diff --git a/substrate/node/consensus/src/lib.rs b/substrate/node/consensus/src/lib.rs deleted file mode 100644 index 2b100c10a8..0000000000 --- a/substrate/node/consensus/src/lib.rs +++ /dev/null @@ -1,533 +0,0 @@ -// 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 . - -//! This service uses BFT consensus provided by the substrate. - -#![cfg(feature="rhd")] - -extern crate node_runtime; -extern crate node_primitives; - -extern crate parity_codec as codec; -extern crate sr_primitives as runtime_primitives; -extern crate srml_system; -extern crate substrate_bft as bft; -extern crate substrate_client as client; -extern crate substrate_primitives as primitives; -extern crate substrate_transaction_pool as transaction_pool; - -extern crate exit_future; -extern crate futures; -extern crate parking_lot; -extern crate rhododendron; -extern crate tokio; - -#[macro_use] -extern crate error_chain; - -#[macro_use] -extern crate log; - -#[cfg(test)] -extern crate substrate_keyring; - -use std::sync::Arc; -use std::time::{self, Duration, Instant}; - -use client::{Client as SubstrateClient, CallExecutor}; -use client::runtime_api::{Core, BlockBuilder as BlockBuilderAPI, Miscellaneous, OldTxQueue}; -use codec::{Decode, Encode}; -use node_primitives::{AccountId, Timestamp, SessionKey}; -use node_runtime::{Runtime, InherentError, TimestampInherentError, InherentData}; -use primitives::{AuthorityId, ed25519, Blake2Hasher}; -use runtime_primitives::traits::{Block as BlockT, Hash as HashT, Header as HeaderT, As, BlockNumberToHash}; -use runtime_primitives::generic::{BlockId, Era}; -use srml_system::Trait as SystemT; -use transaction_pool::txpool::{self, Pool as TransactionPool}; -use tokio::runtime::TaskExecutor; -use tokio::timer::Delay; - -use futures::prelude::*; -use futures::future; -use parking_lot::RwLock; - -pub use self::error::{ErrorKind, Error, Result}; -pub use self::offline_tracker::OfflineTracker; -pub use service::Service; - -mod evaluation; -mod error; -mod service; - -/// Shared offline validator tracker. -pub type SharedOfflineTracker = Arc>; - -// block size limit. -const MAX_TRANSACTIONS_SIZE: usize = 4 * 1024 * 1024; - -/// Build new blocks. -pub trait BlockBuilder { - /// Push an extrinsic onto the block. Fails if the extrinsic is invalid. - fn push_extrinsic(&mut self, extrinsic: ::Extrinsic) -> Result<()>; -} - -/// Local client abstraction for the consensus. -pub trait AuthoringApi: - Send - + Sync - + BlockBuilderAPI<::Block, Error=::Error> - + Core<::Block, AuthorityId, Error=::Error> - + Miscellaneous<::Block, Error=::Error> - + OldTxQueue<::Block, Error=::Error> -{ - /// The block used for this API type. - type Block: BlockT; - /// The error used by this API type. - type Error: std::error::Error; - - /// Build a block on top of the given, with inherent extrinsics pre-pushed. - fn build_block) -> ()>( - &self, - at: &BlockId, - inherent_data: InherentData, - build_ctx: F, - ) -> Result; -} - -impl<'a, B, E, Block> BlockBuilder for client::block_builder::BlockBuilder<'a, B, E, Block, Blake2Hasher> where - B: client::backend::Backend + Send + Sync + 'static, - E: CallExecutor + Send + Sync + Clone + 'static, - Block: BlockT -{ - fn push_extrinsic(&mut self, extrinsic: ::Extrinsic) -> Result<()> { - client::block_builder::BlockBuilder::push(self, extrinsic).map_err(Into::into) - } -} - -impl<'a, B, E, Block> AuthoringApi for SubstrateClient where - B: client::backend::Backend + Send + Sync + 'static, - E: CallExecutor + Send + Sync + Clone + 'static, - Block: BlockT, -{ - type Block = Block; - type Error = client::error::Error; - - fn build_block) -> ()>( - &self, - at: &BlockId, - inherent_data: InherentData, - mut build_ctx: F, - ) -> Result { - let runtime_version = self.runtime_version_at(at)?; - - let mut block_builder = self.new_block_at(at)?; - if runtime_version.has_api(*b"inherent", 1) { - self.inherent_extrinsics(at, &inherent_data)? - .into_iter().try_for_each(|i| block_builder.push(i))?; - } - - build_ctx(&mut block_builder); - - block_builder.bake().map_err(Into::into) - } -} - -/// A long-lived network which can create BFT message routing processes on demand. -pub trait Network { - /// The block used for this API type. - type Block: BlockT; - /// The input stream of BFT messages. Should never logically conclude. - type Input: Stream,Error=Error>; - /// The output sink of BFT messages. Messages sent here should eventually pass to all - /// current authorities. - type Output: Sink,SinkError=Error>; - - /// Instantiate input and output streams. - fn communication_for( - &self, - validators: &[SessionKey], - local_id: SessionKey, - parent_hash: ::Hash, - task_executor: TaskExecutor - ) -> (Self::Input, Self::Output); -} - -/// Proposer factory. -pub struct ProposerFactory where - C: AuthoringApi, - A: txpool::ChainApi, -{ - /// The client instance. - pub client: Arc, - /// The transaction pool. - pub transaction_pool: Arc>, - /// The backing network handle. - pub network: N, - /// handle to remote task executor - pub handle: TaskExecutor, - /// Offline-tracker. - pub offline: SharedOfflineTracker, - /// Force delay in evaluation this long. - pub force_delay: Timestamp, -} - -impl bft::Environment<::Block> for ProposerFactory where - N: Network::Block>, - C: AuthoringApi + BlockNumberToHash, - A: txpool::ChainApi::Block>, - <::Block as BlockT>::Hash: - Into<::Hash> + PartialEq + Into, - Error: From<::Error> -{ - type Proposer = Proposer; - type Input = N::Input; - type Output = N::Output; - type Error = Error; - - fn init( - &self, - parent_header: &<::Block as BlockT>::Header, - authorities: &[AuthorityId], - sign_with: Arc, - ) -> Result<(Self::Proposer, Self::Input, Self::Output)> { - use runtime_primitives::traits::Hash as HashT; - let parent_hash = parent_header.hash(); - - let id = BlockId::hash(parent_hash); - let random_seed = self.client.random_seed(&id)?; - let random_seed = <<::Block as BlockT>::Header as HeaderT>::Hashing::hash(random_seed.as_ref()); - - let validators = self.client.validators(&id)?; - self.offline.write().note_new_block(&validators[..]); - - info!("Starting consensus session on top of parent {:?}", parent_hash); - - let local_id = sign_with.public().0.into(); - let (input, output) = self.network.communication_for( - authorities, - local_id, - parent_hash.clone(), - self.handle.clone(), - ); - let now = Instant::now(); - let proposer = Proposer { - client: self.client.clone(), - start: now, - local_key: sign_with, - next_index: None, - parent_hash, - parent_id: id, - parent_number: *parent_header.number(), - random_seed, - transaction_pool: self.transaction_pool.clone(), - offline: self.offline.clone(), - validators, - minimum_timestamp: current_timestamp() + self.force_delay, - }; - - Ok((proposer, input, output)) - } -} - -/// The proposer logic. -pub struct Proposer { - client: Arc, - start: Instant, - local_key: Arc, - next_index: Option, - parent_hash: <::Block as BlockT>::Hash, - parent_id: BlockId<::Block>, - parent_number: <<::Block as BlockT>::Header as HeaderT>::Number, - random_seed: <::Block as BlockT>::Hash, - transaction_pool: Arc>, - offline: SharedOfflineTracker, - validators: Vec, - minimum_timestamp: u64, -} - -impl Proposer { - fn primary_index(&self, round_number: usize, len: usize) -> usize { - use primitives::uint::U256; - - let big_len = U256::from(len); - let offset = U256::from_big_endian(self.random_seed.as_ref()) % big_len; - let offset = offset.low_u64() as usize + round_number; - offset % len - } -} - -impl bft::Proposer<::Block> for Proposer where - C: AuthoringApi + BlockNumberToHash, - A: txpool::ChainApi::Block>, - <::Block as BlockT>::Hash: - Into<::Hash> + PartialEq + Into, - error::Error: From<::Error> -{ - type Create = Result<::Block>; - type Error = Error; - type Evaluate = Box>; - - fn propose(&self) -> Result<::Block> { - use runtime_primitives::traits::BlakeTwo256; - - const MAX_VOTE_OFFLINE_SECONDS: Duration = Duration::from_secs(60); - - // TODO: handle case when current timestamp behind that in state. - let timestamp = ::std::cmp::max(self.minimum_timestamp, current_timestamp()); - - let elapsed_since_start = self.start.elapsed(); - let offline_indices = if elapsed_since_start > MAX_VOTE_OFFLINE_SECONDS { - Vec::new() - } else { - self.offline.read().reports(&self.validators[..]) - }; - - if !offline_indices.is_empty() { - info!( - "Submitting offline validators {:?} for slash-vote", - offline_indices.iter().map(|&i| self.validators[i as usize]).collect::>(), - ) - } - - let inherent_data = InherentData { - timestamp, - offline_indices, - }; - - let block = self.client.build_block( - &self.parent_id, - inherent_data, - |block_builder| { - let mut unqueue_invalid = Vec::new(); - self.transaction_pool.ready(|pending_iterator| { - let mut pending_size = 0; - for pending in pending_iterator { - // TODO [ToDr] Probably get rid of it, and validate in runtime. - let encoded_size = pending.data.encode().len(); - if pending_size + encoded_size >= MAX_TRANSACTIONS_SIZE { break } - - match block_builder.push_extrinsic(pending.data.clone()) { - Ok(()) => { - pending_size += encoded_size; - } - Err(e) => { - trace!(target: "transaction-pool", "Invalid transaction: {}", e); - unqueue_invalid.push(pending.hash.clone()); - } - } - } - }); - - self.transaction_pool.remove_invalid(&unqueue_invalid); - })?; - - info!("Proposing block [number: {}; hash: {}; parent_hash: {}; extrinsics: [{}]]", - block.header().number(), - <::Block as BlockT>::Hash::from(block.header().hash()), - block.header().parent_hash(), - block.extrinsics().iter() - .map(|xt| format!("{}", BlakeTwo256::hash_of(xt))) - .collect::>() - .join(", ") - ); - - let substrate_block = Decode::decode(&mut block.encode().as_slice()) - .expect("blocks are defined to serialize to substrate blocks correctly; qed"); - - assert!(evaluation::evaluate_initial( - &substrate_block, - &self.parent_hash, - self.parent_number, - ).is_ok()); - - Ok(substrate_block) - } - - fn evaluate(&self, unchecked_proposal: &::Block) -> Self::Evaluate { - debug!(target: "bft", "evaluating block on top of parent ({}, {:?})", self.parent_number, self.parent_hash); - - // do initial serialization and structural integrity checks. - match evaluation::evaluate_initial( - unchecked_proposal, - &self.parent_hash, - self.parent_number, - ) { - Ok(p) => p, - Err(e) => { - // TODO: these errors are easily re-checked in runtime. - debug!(target: "bft", "Invalid proposal (initial evaluation failed): {:?}", e); - return Box::new(future::ok(false)); - } - }; - - let current_timestamp = current_timestamp(); - let inherent = InherentData::new( - current_timestamp, - self.offline.read().reports(&self.validators) - ); - let proposed_timestamp = match self.client.check_inherents( - &self.parent_id, - &unchecked_proposal, - &inherent - ) { - Ok(Ok(())) => None, - Ok(Err(InherentError::Timestamp(TimestampInherentError::TimestampInFuture(timestamp)))) => Some(timestamp), - Ok(Err(e)) => { - debug!(target: "bft", "Invalid proposal (check_inherents): {:?}", e); - return Box::new(future::ok(false)); - }, - Err(e) => { - debug!(target: "bft", "Could not call into runtime: {:?}", e); - return Box::new(future::ok(false)); - } - }; - - let vote_delays = { - // the duration until the given timestamp is current - let proposed_timestamp = ::std::cmp::max(self.minimum_timestamp, proposed_timestamp.unwrap_or(0)); - let timestamp_delay = if proposed_timestamp > current_timestamp { - let delay_s = proposed_timestamp - current_timestamp; - debug!(target: "bft", "Delaying evaluation of proposal for {} seconds", delay_s); - Some(Instant::now() + Duration::from_secs(delay_s)) - } else { - None - }; - - match timestamp_delay { - Some(duration) => future::Either::A( - Delay::new(duration).map_err(|e| ErrorKind::Timer(e).into()) - ), - None => future::Either::B(future::ok(())), - } - }; - - // evaluate whether the block is actually valid. - // TODO: is it better to delay this until the delays are finished? - let evaluated = match self.client.execute_block(&self.parent_id, &unchecked_proposal.clone()).map_err(Error::from) { - Ok(()) => Ok(true), - Err(err) => match err.kind() { - error::ErrorKind::Client(client::error::ErrorKind::Execution(_)) => Ok(false), - _ => Err(err) - } - }; - - let future = future::result(evaluated).and_then(move |good| { - let end_result = future::ok(good); - if good { - // delay a "good" vote. - future::Either::A(vote_delays.and_then(|_| end_result)) - } else { - // don't delay a "bad" evaluation. - future::Either::B(end_result) - } - }); - - Box::new(future) as Box<_> - } - - fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId { - let offset = self.primary_index(round_number, authorities.len()); - let proposer = authorities[offset].clone(); - trace!(target: "bft", "proposer for round {} is {}", round_number, proposer); - - proposer - } - - fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, bft::Misbehavior<<::Block as BlockT>::Hash>)>) { - use rhododendron::Misbehavior as GenericMisbehavior; - use runtime_primitives::bft::{MisbehaviorKind, MisbehaviorReport}; - use node_runtime::{Call, UncheckedExtrinsic, ConsensusCall}; - - let mut next_index = { - let cur_index: Result = self.client - .account_nonce(&self.parent_id, &self.local_key.public().0) - .map_err(Error::from); - - let cur_index = match cur_index { - Ok(cur_index) => cur_index + 1, - Err(e) => { - warn!(target: "consensus", "Error computing next transaction index: {:?}", e); - return; - } - }; - - let index = match self.next_index { - // make sure there were no other transactions in the meantime - Some(idx) if idx > cur_index => idx, - _ => cur_index, - }; - - index - }; - - for (target, misbehavior) in misbehavior { - let report = MisbehaviorReport { - parent_hash: self.parent_hash.into(), - parent_number: self.parent_number.as_(), - target, - misbehavior: match misbehavior { - GenericMisbehavior::ProposeOutOfTurn(_, _, _) => continue, - GenericMisbehavior::DoublePropose(_, _, _) => continue, - GenericMisbehavior::DoublePrepare(round, (h1, s1), (h2, s2)) - => MisbehaviorKind::BftDoublePrepare(round as u32, (h1.into(), s1.signature), (h2.into(), s2.signature)), - GenericMisbehavior::DoubleCommit(round, (h1, s1), (h2, s2)) - => MisbehaviorKind::BftDoubleCommit(round as u32, (h1.into(), s1.signature), (h2.into(), s2.signature)), - } - }; - let payload = (next_index, Call::Consensus(ConsensusCall::report_misbehavior(report)), Era::immortal(), self.client.genesis_hash()); - let signature = self.local_key.sign(&payload.encode()).into(); - next_index += 1; - self.next_index = Some(next_index); - - let local_id = self.local_key.public().0.into(); - let extrinsic = UncheckedExtrinsic { - signature: Some((node_runtime::RawAddress::Id(local_id), signature, payload.0, Era::immortal())), - function: payload.1, - }; - let uxt: <::Block as BlockT>::Extrinsic = Decode::decode(&mut extrinsic.encode().as_slice()).expect("Encoded extrinsic is valid"); - let hash = BlockId::<::Block>::hash(self.parent_hash); - if let Err(e) = self.transaction_pool.submit_one(&hash, uxt) { - warn!("Error importing misbehavior report: {:?}", e); - } - } - } - - fn on_round_end(&self, round_number: usize, was_proposed: bool) { - let primary_validator = self.validators[ - self.primary_index(round_number, self.validators.len()) - ]; - - - // alter the message based on whether we think the empty proposer was forced to skip the round. - // this is determined by checking if our local validator would have been forced to skip the round. - if !was_proposed { - let public = ed25519::Public::from_raw(primary_validator.0); - info!( - "Potential Offline Validator: {} failed to propose during assigned slot: {}", - public, - round_number, - ); - } - - self.offline.write().note_round_end(primary_validator, was_proposed); - } -} - -fn current_timestamp() -> Timestamp { - time::SystemTime::now().duration_since(time::UNIX_EPOCH) - .expect("now always later than unix epoch; qed") - .as_secs() -} diff --git a/substrate/node/executor/src/lib.rs b/substrate/node/executor/src/lib.rs index 6a2b8061c3..bdd8b10235 100644 --- a/substrate/node/executor/src/lib.rs +++ b/substrate/node/executor/src/lib.rs @@ -254,6 +254,7 @@ mod tests { timestamp: Some(Default::default()), treasury: Some(Default::default()), contract: Some(Default::default()), + upgrade_key: Some(Default::default()), }.build_storage().unwrap()) } @@ -291,9 +292,9 @@ mod tests { 1, GENESIS_HASH.into(), if support_changes_trie { - hex!("ffa85ed1832eae3e25e684d4f993ff0b5e8b6ac4d7ba0f40a5fb0114fda22f3d").into() + hex!("978a3ff733a86638da39d36a349c693b5cf562bcc8db30fec6c2b6c40f925a9b").into() } else { - hex!("98971908b8923d07944cdf7ee658c203d17042ef447169adbdfec8160cfabcad").into() + hex!("7bbad534e3de3db3c8cda015c4e8ed8ba10dde7e3fca21f4fd4fbc686e6c1410").into() }, if support_changes_trie { Some(hex!("1f8f44dcae8982350c14dee720d34b147e73279f5a2ce1f9781195a991970978").into()) @@ -317,7 +318,7 @@ mod tests { construct_block( 2, block1(false).1, - hex!("788a2e8b23e4b30e1bce347ca6415fd0080e989d40741c86995b9ad539bb76b3").into(), + hex!("7be30152ee2ee909047cffad5f0a28bf8c2b0a97c124b500aeac112f6917738e").into(), None, vec![ CheckedExtrinsic { @@ -340,7 +341,7 @@ mod tests { construct_block( 1, GENESIS_HASH.into(), - hex!("acc03af5b3972deaf9dde4dfd99c5614a5360454313681b6fc299d1644ae8a59").into(), + hex!("325a73726dc640af41becb42938e7152e218f130219c0695aae35b6a156f93f3").into(), None, vec![ CheckedExtrinsic { @@ -622,7 +623,7 @@ mod tests { let b = construct_block( 1, GENESIS_HASH.into(), - hex!("21fb6fb965f012ae3c6e521b71b5b57d6df17c738c52f202ec2809ca235eb082").into(), + hex!("d68586d5098535e04ff7a12d71a9c9dc719960f318862e636e78a8e98cf4b8d4").into(), None, vec![ CheckedExtrinsic { diff --git a/substrate/node/network/Cargo.toml b/substrate/node/network/Cargo.toml deleted file mode 100644 index 44fbdb5aba..0000000000 --- a/substrate/node/network/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "node-network" -version = "0.1.0" -authors = ["Parity Technologies "] -description = "Substrate node networking protocol" - -[dependencies] -node-consensus = { path = "../consensus" } -node-primitives = { path = "../primitives" } -substrate-consensus-rhd = { path = "../../core/consensus/rhd" } -substrate-network = { path = "../../core/network" } -substrate-primitives = { path = "../../core/primitives" } -futures = "0.1" -tokio = "0.1.7" -log = "0.4" -rhododendron = "0.3" diff --git a/substrate/node/network/src/consensus.rs b/substrate/node/network/src/consensus.rs deleted file mode 100644 index 44d338d330..0000000000 --- a/substrate/node/network/src/consensus.rs +++ /dev/null @@ -1,293 +0,0 @@ -// 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 . - -//! The "consensus" networking code built on top of the base network service. -//! This fulfills the `node_consensus::Network` trait, providing a hook to be called -//! each time consensus begins on a new chain head. - -use bft; -use substrate_primitives::ed25519; -use substrate_network::{self as net, generic_message as msg}; -use substrate_network::consensus_gossip::ConsensusMessage; -use node_consensus::{AuthoringApi, Network}; -use node_primitives::{Block, Hash, SessionKey}; -use rhododendron; - -use futures::prelude::*; -use futures::sync::mpsc; - -use std::sync::Arc; - -use tokio::runtime::TaskExecutor; -use tokio::executor::Executor; - -use super::NetworkService; - -/// Sink for output BFT messages. -pub struct BftSink { - network: Arc, - parent_hash: Hash, - _marker: ::std::marker::PhantomData, -} - -impl Sink for BftSink { - type SinkItem = bft::Communication; - // TODO: replace this with the ! type when that's stabilized - type SinkError = E; - - fn start_send(&mut self, message: bft::Communication) - -> ::futures::StartSend, E> - { - let network_message = net::LocalizedBftMessage { - message: match message { - rhododendron::Communication::Consensus(c) => msg::BftMessage::Consensus(match c { - rhododendron::LocalizedMessage::Propose(proposal) => msg::SignedConsensusMessage::Propose(msg::SignedConsensusProposal { - round_number: proposal.round_number as u32, - proposal: proposal.proposal, - digest: proposal.digest, - sender: proposal.sender, - digest_signature: proposal.digest_signature.signature, - full_signature: proposal.full_signature.signature, - }), - rhododendron::LocalizedMessage::Vote(vote) => msg::SignedConsensusMessage::Vote(msg::SignedConsensusVote { - sender: vote.sender, - signature: vote.signature.signature, - vote: match vote.vote { - rhododendron::Vote::Prepare(r, h) => msg::ConsensusVote::Prepare(r as u32, h), - rhododendron::Vote::Commit(r, h) => msg::ConsensusVote::Commit(r as u32, h), - rhododendron::Vote::AdvanceRound(r) => msg::ConsensusVote::AdvanceRound(r as u32), - } - }), - }), - rhododendron::Communication::Auxiliary(justification) => { - let unchecked: bft::UncheckedJustification<_> = justification.uncheck().into(); - msg::BftMessage::Auxiliary(unchecked.into()) - } - }, - parent_hash: self.parent_hash, - }; - self.network.with_spec( - move |spec, ctx| spec.consensus_gossip.multicast_bft_message(ctx, network_message) - ); - Ok(::futures::AsyncSink::Ready) - } - - fn poll_complete(&mut self) -> ::futures::Poll<(), E> { - Ok(Async::Ready(())) - } -} - -// check signature and authority validity of message. -fn process_bft_message( - msg: msg::LocalizedBftMessage, - local_id: &SessionKey, - authorities: &[SessionKey] - ) -> Result>, bft::Error> -{ - Ok(Some(match msg.message { - msg::BftMessage::Consensus(c) => rhododendron::Communication::Consensus(match c { - msg::SignedConsensusMessage::Propose(proposal) => rhododendron::LocalizedMessage::Propose({ - if &proposal.sender == local_id { return Ok(None) } - let proposal = rhododendron::LocalizedProposal { - round_number: proposal.round_number as usize, - proposal: proposal.proposal, - digest: proposal.digest, - sender: proposal.sender, - digest_signature: ed25519::LocalizedSignature { - signature: proposal.digest_signature, - signer: ed25519::Public(proposal.sender.into()), - }, - full_signature: ed25519::LocalizedSignature { - signature: proposal.full_signature, - signer: ed25519::Public(proposal.sender.into()), - } - }; - bft::check_proposal(authorities, &msg.parent_hash, &proposal)?; - - trace!(target: "bft", "importing proposal message for round {} from {}", proposal.round_number, Hash::from(proposal.sender.0)); - proposal - }), - msg::SignedConsensusMessage::Vote(vote) => rhododendron::LocalizedMessage::Vote({ - if &vote.sender == local_id { return Ok(None) } - let vote = rhododendron::LocalizedVote { - sender: vote.sender, - signature: ed25519::LocalizedSignature { - signature: vote.signature, - signer: ed25519::Public(vote.sender.0), - }, - vote: match vote.vote { - msg::ConsensusVote::Prepare(r, h) => rhododendron::Vote::Prepare(r as usize, h), - msg::ConsensusVote::Commit(r, h) => rhododendron::Vote::Commit(r as usize, h), - msg::ConsensusVote::AdvanceRound(r) => rhododendron::Vote::AdvanceRound(r as usize), - } - }; - bft::check_vote::(authorities, &msg.parent_hash, &vote)?; - - trace!(target: "bft", "importing vote {:?} from {}", vote.vote, Hash::from(vote.sender.0)); - vote - }), - }), - msg::BftMessage::Auxiliary(a) => { - let justification = bft::UncheckedJustification::from(a); - // TODO: get proper error - let justification: Result<_, bft::Error> = bft::check_prepare_justification::(authorities, msg.parent_hash, justification) - .map_err(|_| bft::ErrorKind::InvalidJustification.into()); - rhododendron::Communication::Auxiliary(justification?) - }, - })) -} - -// task that processes all gossipped consensus messages, -// checking signatures -struct MessageProcessTask { - inner_stream: mpsc::UnboundedReceiver>, - bft_messages: mpsc::UnboundedSender>, - validators: Vec, - local_id: SessionKey, -} - -impl MessageProcessTask { - fn process_message(&self, msg: ConsensusMessage) -> Option> { - match msg { - ConsensusMessage::Bft(msg) => { - match process_bft_message(msg, &self.local_id, &self.validators[..]) { - Ok(Some(msg)) => { - if let Err(_) = self.bft_messages.unbounded_send(msg) { - // if the BFT receiving stream has ended then - // we should just bail. - trace!(target: "bft", "BFT message stream appears to have closed"); - return Some(Async::Ready(())); - } - } - Ok(None) => {} // ignored local message - Err(e) => { - debug!("Message validation failed: {:?}", e); - } - } - } - ConsensusMessage::ChainSpecific(_, _) => { - panic!("ChainSpecific messages are not allowed by the top level message handler"); - } - } - - None - } -} - -impl Future for MessageProcessTask { - type Item = (); - type Error = (); - - fn poll(&mut self) -> Poll<(), ()> { - loop { - match self.inner_stream.poll() { - Ok(Async::Ready(Some(val))) => if let Some(async) = self.process_message(val) { - return Ok(async); - }, - Ok(Async::Ready(None)) => return Ok(Async::Ready(())), - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(e) => { - debug!(target: "node-network", "Error getting consensus message: {:?}", e); - return Err(e); - }, - } - } - } -} - -/// Input stream from the consensus network. -pub struct InputAdapter { - input: mpsc::UnboundedReceiver>, -} - -impl Stream for InputAdapter { - type Item = bft::Communication; - type Error = ::node_consensus::Error; - - fn poll(&mut self) -> Poll, Self::Error> { - match self.input.poll() { - Err(_) | Ok(Async::Ready(None)) => Err(bft::InputStreamConcluded.into()), - Ok(x) => Ok(x) - } - } -} - -/// Wrapper around the network service -pub struct ConsensusNetwork

{ - network: Arc, - api: Arc

, -} - -impl

ConsensusNetwork

{ - /// Create a new consensus networking object. - pub fn new(network: Arc, api: Arc

) -> Self { - ConsensusNetwork { network, api } - } -} - -impl

Clone for ConsensusNetwork

{ - fn clone(&self) -> Self { - ConsensusNetwork { - network: self.network.clone(), - api: self.api.clone(), - } - } -} - -/// A long-lived network which can create parachain statement and BFT message routing processes on demand. -impl Network for ConsensusNetwork

{ - /// The input stream of BFT messages. Should never logically conclude. - type Input = InputAdapter; - /// The output sink of BFT messages. Messages sent here should eventually pass to all - /// current validators. - type Output = BftSink<::node_consensus::Error>; - type Block = Block; - - /// Get input and output streams of BFT messages. - fn communication_for( - &self, validators: &[SessionKey], - local_id: SessionKey, - parent_hash: Hash, - mut task_executor: TaskExecutor - ) -> (Self::Input, Self::Output) - { - let sink = BftSink { - network: self.network.clone(), - parent_hash, - _marker: Default::default(), - }; - - let (bft_send, bft_recv) = mpsc::unbounded(); - - // spin up a task in the background that processes all incoming statements - // TODO: propagate statements on a timer? - let process_task = self.network.with_spec(|spec, _ctx| { - spec.consensus_gossip.new_session(parent_hash); - MessageProcessTask { - inner_stream: spec.consensus_gossip.messages_for(parent_hash), - bft_messages: bft_send, - validators: validators.to_vec(), - local_id, - } - }); - - if let Err(e) = Executor::spawn(&mut task_executor, Box::new(process_task)) { - debug!(target: "node-network", "Cannot spawn message processing: {:?}", e) - } - - (InputAdapter { input: bft_recv }, sink) - } -} diff --git a/substrate/node/network/src/lib.rs b/substrate/node/network/src/lib.rs deleted file mode 100644 index 9e2f96d5f7..0000000000 --- a/substrate/node/network/src/lib.rs +++ /dev/null @@ -1,38 +0,0 @@ -// 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 . - -//! Substrate-specific network implementation. -//! -//! This manages gossip of consensus messages for BFT. - -#![warn(unused_extern_crates)] - -#[macro_use] -extern crate substrate_network; -extern crate node_primitives; - -use node_primitives::{Block, Hash}; -use substrate_network::consensus_gossip::ConsensusGossip; - -/// Specialization of the network service for the node protocol. -pub type NetworkService = ::substrate_network::Service; - -construct_simple_protocol! { - /// Demo protocol attachment for substrate. - pub struct Protocol where Block = Block { - consensus_gossip: ConsensusGossip, - } -} diff --git a/substrate/node/runtime/Cargo.toml b/substrate/node/runtime/Cargo.toml index ac37cff456..8add782018 100644 --- a/substrate/node/runtime/Cargo.toml +++ b/substrate/node/runtime/Cargo.toml @@ -29,6 +29,7 @@ srml-staking = { path = "../../srml/staking" } srml-system = { path = "../../srml/system" } srml-timestamp = { path = "../../srml/timestamp" } srml-treasury = { path = "../../srml/treasury" } +srml-upgrade-key = { path = "../../srml/upgrade-key" } sr-version = { path = "../../core/sr-version" } node-primitives = { path = "../primitives" } @@ -53,6 +54,7 @@ std = [ "srml-system/std", "srml-timestamp/std", "srml-treasury/std", + "srml-upgrade-key/std", "sr-version/std", "node-primitives/std", "serde_derive", diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index 1f4a44bb29..998bb6640b 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -52,6 +52,7 @@ extern crate srml_staking as staking; extern crate srml_system as system; extern crate srml_timestamp as timestamp; extern crate srml_treasury as treasury; +extern crate srml_upgrade_key as upgrade_key; #[macro_use] extern crate sr_version as version; extern crate node_primitives; @@ -62,12 +63,12 @@ use node_primitives::{ AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, SessionKey, Signature }; -use runtime_api::runtime::*; +use runtime_api::{runtime::*, id::*}; use runtime_primitives::ApplyResult; use runtime_primitives::transaction_validity::TransactionValidity; use runtime_primitives::generic; use runtime_primitives::traits::{Convert, BlakeTwo256, Block as BlockT}; -use version::{RuntimeVersion, ApiId}; +use version::RuntimeVersion; use council::{motions as council_motions, voting as council_voting}; #[cfg(feature = "std")] use council::seats as council_seats; @@ -86,9 +87,6 @@ pub use srml_support::{StorageValue, RuntimeMetadata}; const TIMESTAMP_SET_POSITION: u32 = 0; const NOTE_OFFLINE_POSITION: u32 = 1; -const INHERENT: ApiId = *b"inherent"; -const VALIDATX: ApiId = *b"validatx"; - /// Runtime version. pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: ver_str!("node"), @@ -96,7 +94,11 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { authoring_version: 1, spec_version: 1, impl_version: 0, - apis: apis_vec!([(INHERENT, 1), (VALIDATX, 1)]), + apis: apis_vec!([ + (BLOCK_BUILDER, 1), + (TAGGED_TRANSACTION_QUEUE, 1), + (METADATA, 1) + ]), }; /// Native version. @@ -191,15 +193,19 @@ impl contract::Trait for Runtime { type Event = Event; } +impl upgrade_key::Trait for Runtime { + type Event = Event; +} + construct_runtime!( pub enum Runtime with Log(InternalLog: DigestItem) where Block = Block, UncheckedExtrinsic = UncheckedExtrinsic { System: system::{default, Log(ChangesTrieRoot)}, + Timestamp: timestamp::{Module, Call, Storage, Config, Inherent}, Consensus: consensus::{Module, Call, Storage, Config, Log(AuthoritiesChange), Inherent}, Balances: balances, - Timestamp: timestamp::{Module, Call, Storage, Config, Inherent}, Session: session, Staking: staking, Democracy: democracy, @@ -209,6 +215,7 @@ construct_runtime!( CouncilSeats: council_seats::{Config}, Treasury: treasury, Contract: contract::{Module, Call, Config, Event}, + UpgradeKey: upgrade_key, } ); @@ -278,33 +285,9 @@ impl_apis! { } } - impl OldTxQueue for Runtime { - fn account_nonce(account: AccountId) -> Index { - System::account_nonce(&account) - } - - fn lookup_address(address: Address) -> Option { - Balances::lookup_address(address) - } - } - impl TaggedTransactionQueue for Runtime { fn validate_transaction(tx: ::Extrinsic) -> TransactionValidity { Executive::validate_transaction(tx) } } - - impl Miscellaneous for Runtime { - fn validator_count() -> u32 { - Session::validator_count() - } - - fn validators() -> Vec { - Session::validators() - } - - fn timestamp() -> u64 { - Timestamp::get() - } - } } diff --git a/substrate/node/runtime/wasm/Cargo.lock b/substrate/node/runtime/wasm/Cargo.lock index fc45e6f94c..0aa58928c2 100644 --- a/substrate/node/runtime/wasm/Cargo.lock +++ b/substrate/node/runtime/wasm/Cargo.lock @@ -285,6 +285,7 @@ dependencies = [ "srml-system 0.1.0", "srml-timestamp 0.1.0", "srml-treasury 0.1.0", + "srml-upgrade-key 0.1.0", "substrate-primitives 0.1.0", ] @@ -817,6 +818,24 @@ dependencies = [ "substrate-primitives 0.1.0", ] +[[package]] +name = "srml-upgrade-key" +version = "0.1.0" +dependencies = [ + "hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 0.1.0", + "sr-primitives 0.1.0", + "sr-std 0.1.0", + "srml-consensus 0.1.0", + "srml-support 0.1.0", + "srml-system 0.1.0", + "substrate-primitives 0.1.0", +] + [[package]] name = "stable_deref_trait" version = "1.1.1" diff --git a/substrate/node/runtime/wasm/Cargo.toml b/substrate/node/runtime/wasm/Cargo.toml index 7fb33ff26b..7df582a8f0 100644 --- a/substrate/node/runtime/wasm/Cargo.toml +++ b/substrate/node/runtime/wasm/Cargo.toml @@ -28,6 +28,7 @@ srml-staking = { path = "../../../srml/staking", default-features = false } srml-system = { path = "../../../srml/system", default-features = false } srml-timestamp = { path = "../../../srml/timestamp", default-features = false } srml-treasury = { path = "../../../srml/treasury", default-features = false } +srml-upgrade-key = { path = "../../../srml/upgrade-key", default-features = false } sr-version = { path = "../../../core/sr-version", default-features = false } node-primitives = { path = "../../primitives", default-features = false } @@ -53,6 +54,7 @@ std = [ "srml-system/std", "srml-timestamp/std", "srml-treasury/std", + "srml-upgrade-key/std", "sr-version/std", "node-primitives/std", ] diff --git a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm index 3ffbf0d1b1..2a48d20c98 100644 Binary files a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm and b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm differ diff --git a/substrate/srml/balances/src/lib.rs b/substrate/srml/balances/src/lib.rs index 83eb408132..224d02c0d1 100644 --- a/substrate/srml/balances/src/lib.rs +++ b/substrate/srml/balances/src/lib.rs @@ -423,6 +423,23 @@ impl Module { Self::set_free_balance_creating(who, Self::free_balance(who) + value) } + /// Substrates `value` from the free balance of `who`. If the whole amount cannot be + /// deducted, an error is returned. + /// + /// NOTE: This assumes that the total stake remains unchanged after this operation. If + /// you mean to actually burn value out of existence, then use `slash` instead. + pub fn decrease_free_balance( + who: &T::AccountId, + value: T::Balance + ) -> result::Result { + T::EnsureAccountLiquid::ensure_account_liquid(who)?; + let b = Self::free_balance(who); + if b < value { + return Err("account has too few funds") + } + Ok(Self::set_free_balance(who, b - value)) + } + /// Deducts up to `value` from the combined balance of `who`, preferring to deduct from the /// free balance. This function cannot fail. /// diff --git a/substrate/srml/balances/src/tests.rs b/substrate/srml/balances/src/tests.rs index b9c5b85902..9ad100fe61 100644 --- a/substrate/srml/balances/src/tests.rs +++ b/substrate/srml/balances/src/tests.rs @@ -280,6 +280,17 @@ fn balance_transfer_works() { }); } +#[test] +fn balance_reduction_works() { + with_externalities(&mut ExtBuilder::default().build(), || { + Balances::set_free_balance(&1, 111); + Balances::increase_total_stake_by(111); + assert_ok!(Balances::decrease_free_balance(&1, 69).map(|_| ())); + assert_eq!(Balances::total_balance(&1), 42); + assert_noop!(Balances::decrease_free_balance(&1, 69).map(|_| ()), "account has too few funds"); + }); +} + #[test] fn reserving_balance_should_work() { with_externalities(&mut ExtBuilder::default().build(), || { diff --git a/substrate/srml/consensus/src/lib.rs b/substrate/srml/consensus/src/lib.rs index 6975fe792c..1c46e38541 100644 --- a/substrate/srml/consensus/src/lib.rs +++ b/substrate/srml/consensus/src/lib.rs @@ -29,6 +29,7 @@ extern crate srml_support as runtime_support; #[macro_use] extern crate serde_derive; +extern crate parity_codec; #[macro_use] extern crate parity_codec_derive; @@ -42,6 +43,7 @@ extern crate sr_io as runtime_io; use rstd::prelude::*; use rstd::result; +use parity_codec::Encode; use runtime_support::{storage, Parameter}; use runtime_support::dispatch::Result; use runtime_support::storage::StorageValue; @@ -174,8 +176,14 @@ decl_module! { Ok(()) } + /// Set the number of pages in the WebAssembly environment's heap. + fn set_heap_pages(pages: u64) -> Result { + storage::unhashed::put_raw(well_known_keys::HEAP_PAGES, &pages.encode()); + Ok(()) + } + /// Set the new code. - fn set_code(new: Vec) -> Result { + pub fn set_code(new: Vec) -> Result { storage::unhashed::put_raw(well_known_keys::CODE, &new); Ok(()) } diff --git a/substrate/srml/contract/src/lib.rs b/substrate/srml/contract/src/lib.rs index 30e82f4923..5e3722bb04 100644 --- a/substrate/srml/contract/src/lib.rs +++ b/substrate/srml/contract/src/lib.rs @@ -154,6 +154,7 @@ decl_module! { fn deposit_event() = default; // TODO: Change AccountId to staking::Address /// Make a call to a specified account, optionally transferring some balance. + /// Make a call to a specified account, optionally transferring some balance. fn call( origin, dest: T::AccountId, @@ -232,12 +233,14 @@ decl_module! { }; let result = ctx.create(origin.clone(), endowment, &mut gas_meter, &ctor_code, &data); - if let Ok(_) = result { + if let Ok(ref r) = result { // Commit all changes that made it thus far into the persistant storage. account_db::DirectAccountDb.commit(ctx.overlay.into_change_set()); // Then deposit all events produced. ctx.events.into_iter().for_each(Self::deposit_event); + + Self::deposit_event(RawEvent::Created(origin.clone(), r.address.clone())); } // Refund cost of the unused gas. @@ -263,6 +266,9 @@ decl_event! { { /// Transfer happened `from` -> `to` with given `value` as part of a `message-call` or `create`. Transfer(AccountId, AccountId, Balance), + + /// Contract deployed by address at the specified address. + Created(AccountId, AccountId), } } diff --git a/substrate/srml/contract/src/tests.rs b/substrate/srml/contract/src/tests.rs index f48769c978..9e2d2f1f68 100644 --- a/substrate/srml/contract/src/tests.rs +++ b/substrate/srml/contract/src/tests.rs @@ -617,6 +617,10 @@ fn top_level_create() { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::Transfer(0, derived_address, 11)), }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contract(RawEvent::Created(0, 1)), + }, ]); }); } diff --git a/substrate/srml/support/src/dispatch.rs b/substrate/srml/support/src/dispatch.rs index 8a131336ae..dd52e35b3c 100644 --- a/substrate/srml/support/src/dispatch.rs +++ b/substrate/srml/support/src/dispatch.rs @@ -208,7 +208,7 @@ macro_rules! decl_module { [ $($t)* $(#[doc = $doc_attr])* - fn $fn_name(root $( , $param_name : $param )* ) -> $result { $( $impl )* } + $fn_vis fn $fn_name(root $( , $param_name : $param )* ) -> $result { $( $impl )* } ] $($rest)* ); diff --git a/substrate/srml/upgrade-key/Cargo.toml b/substrate/srml/upgrade-key/Cargo.toml new file mode 100644 index 0000000000..980a7240a9 --- /dev/null +++ b/substrate/srml/upgrade-key/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "srml-upgrade-key" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +hex-literal = "0.1.0" +serde = { version = "1.0", default-features = false } +serde_derive = { version = "1.0", optional = true } +parity-codec = { version = "2.1", default-features = false } +parity-codec-derive = { version = "2.1", default-features = false } +substrate-primitives = { path = "../../core/primitives", default-features = false } +sr-std = { path = "../../core/sr-std", default-features = false } +sr-io = { path = "../../core/sr-io", default-features = false } +sr-primitives = { path = "../../core/sr-primitives", default-features = false } +srml-support = { path = "../support", default-features = false } +srml-system = { path = "../system", default-features = false } +srml-consensus = { path = "../consensus", default-features = false } + +[features] +default = ["std"] +std = [ + "serde/std", + "serde_derive", + "parity-codec/std", + "parity-codec-derive/std", + "sr-std/std", + "sr-io/std", + "sr-primitives/std", + "substrate-primitives/std", + "srml-support/std", + "srml-system/std", + "srml-consensus/std", +] diff --git a/substrate/srml/upgrade-key/src/lib.rs b/substrate/srml/upgrade-key/src/lib.rs new file mode 100644 index 0000000000..28433d8fbd --- /dev/null +++ b/substrate/srml/upgrade-key/src/lib.rs @@ -0,0 +1,90 @@ +// Copyright 2017-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 . + +//! The Example: A simple example of a runtime module demonstrating +//! concepts, APIs and structures common to most runtime modules. + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate sr_std; +#[cfg(test)] +extern crate sr_io; +#[cfg(test)] +extern crate substrate_primitives; +extern crate sr_primitives; +#[cfg(feature = "std")] +#[macro_use] +extern crate serde_derive; +#[macro_use] +extern crate parity_codec_derive; +extern crate parity_codec as codec; +#[macro_use] +extern crate srml_support as support; +extern crate srml_system as system; +extern crate srml_consensus as consensus; + +use sr_std::prelude::*; +use support::{StorageValue, dispatch::Result}; +use system::ensure_signed; + +pub trait Trait: consensus::Trait + system::Trait { + /// The overarching event type. + type Event: From> + Into<::Event>; +} + +decl_module! { + // Simple declaration of the `Module` type. Lets the macro know what its working on. + pub struct Module for enum Call where origin: T::Origin { + fn deposit_event() = default; + fn upgrade(origin, new: Vec) -> Result { + // This is a public call, so we ensure that the origin is some signed account. + let _sender = ensure_signed(origin)?; + ensure!(_sender == Self::key(), "only the current upgrade key can use the upgrade_key module"); + + >::set_code(new)?; + Self::deposit_event(RawEvent::Upgraded); + + Ok(()) + } + + fn set_key(origin, new: T::AccountId) -> Result { + // This is a public call, so we ensure that the origin is some signed account. + let _sender = ensure_signed(origin)?; + ensure!(_sender == Self::key(), "only the current upgrade key can use the upgrade_key module"); + + Self::deposit_event(RawEvent::KeyChanged(Self::key())); + >::put(new); + + Ok(()) + } + } +} + +/// An event in this module. +decl_event!( + pub enum Event where AccountId = ::AccountId { + /// An upgrade just happened. + Upgraded, + /// An upgrade just happened; old key is supplied as an argument. + KeyChanged(AccountId), + } +); + +decl_storage! { + trait Store for Module as UpgradeKey { + Key get(key) config(): T::AccountId; + } +}