diff --git a/Cargo.lock b/Cargo.lock index 24e830af718dd..62969a0efa4dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -695,6 +695,19 @@ dependencies = [ "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "derive_more" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "difference" version = "2.0.0" @@ -2264,7 +2277,9 @@ dependencies = [ "srml-balances 2.0.0", "srml-contracts 2.0.0", "srml-finality-tracker 2.0.0", + "srml-im-online 0.1.0", "srml-indices 2.0.0", + "srml-support 2.0.0", "srml-system 2.0.0", "srml-timestamp 2.0.0", "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2285,6 +2300,7 @@ dependencies = [ "substrate-service-test 2.0.0", "substrate-telemetry 2.0.0", "substrate-transaction-pool 2.0.0", + "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "transaction-factory 0.0.1", ] @@ -2369,6 +2385,7 @@ dependencies = [ "srml-grandpa 2.0.0", "srml-im-online 0.1.0", "srml-indices 2.0.0", + "srml-membership 2.0.0", "srml-session 2.0.0", "srml-staking 2.0.0", "srml-sudo 2.0.0", @@ -2382,6 +2399,7 @@ dependencies = [ "substrate-keyring 2.0.0", "substrate-offchain-primitives 2.0.0", "substrate-primitives 2.0.0", + "substrate-session 2.0.0", "substrate-wasm-builder-runner 1.0.2", ] @@ -2396,13 +2414,15 @@ dependencies = [ "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "node-template-runtime 2.0.0", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "substrate-basic-authorship 2.0.0", "substrate-cli 2.0.0", "substrate-client 2.0.0", "substrate-consensus-aura 2.0.0", + "substrate-consensus-aura-primitives 2.0.0", "substrate-executor 2.0.0", + "substrate-finality-grandpa-primitives 2.0.0", "substrate-inherents 2.0.0", "substrate-network 2.0.0", "substrate-primitives 2.0.0", @@ -2436,6 +2456,7 @@ dependencies = [ "substrate-consensus-aura-primitives 2.0.0", "substrate-offchain-primitives 2.0.0", "substrate-primitives 2.0.0", + "substrate-session 2.0.0", "substrate-wasm-builder-runner 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3631,10 +3652,12 @@ dependencies = [ "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "paste 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "primitive-types 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-std 2.0.0", + "substrate-application-crypto 2.0.0", "substrate-primitives 2.0.0", ] @@ -3689,7 +3712,7 @@ version = "2.0.0" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", @@ -3699,6 +3722,7 @@ dependencies = [ "srml-support 2.0.0", "srml-system 2.0.0", "srml-timestamp 2.0.0", + "substrate-application-crypto 2.0.0", "substrate-consensus-aura-primitives 2.0.0", "substrate-inherents 2.0.0", "substrate-primitives 2.0.0", @@ -3724,7 +3748,7 @@ dependencies = [ "hex-literal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", @@ -3918,6 +3942,7 @@ dependencies = [ "srml-session 2.0.0", "srml-support 2.0.0", "srml-system 2.0.0", + "substrate-application-crypto 2.0.0", "substrate-primitives 2.0.0", ] @@ -3938,6 +3963,20 @@ dependencies = [ "substrate-primitives 2.0.0", ] +[[package]] +name = "srml-membership" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 2.0.0", + "sr-primitives 2.0.0", + "sr-std 2.0.0", + "srml-support 2.0.0", + "srml-system 2.0.0", + "substrate-primitives 2.0.0", +] + [[package]] name = "srml-metadata" version = "2.0.0" @@ -3962,6 +4001,7 @@ dependencies = [ "srml-support 2.0.0", "srml-system 2.0.0", "srml-timestamp 2.0.0", + "substrate-application-crypto 2.0.0", "substrate-primitives 2.0.0", "substrate-trie 2.0.0", ] @@ -4214,6 +4254,19 @@ dependencies = [ "vergen 3.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "substrate-application-crypto" +version = "2.0.0" +dependencies = [ + "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-io 2.0.0", + "sr-primitives 2.0.0", + "sr-std 2.0.0", + "substrate-primitives 2.0.0", + "substrate-test-runtime-client 2.0.0", +] + [[package]] name = "substrate-basic-authorship" version = "2.0.0" @@ -4291,7 +4344,7 @@ dependencies = [ "kvdb-memorydb 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-api-macros 2.0.0", "sr-primitives 2.0.0", "sr-std 2.0.0", @@ -4319,7 +4372,7 @@ dependencies = [ "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-client 2.0.0", "substrate-consensus-common 2.0.0", @@ -4342,12 +4395,13 @@ dependencies = [ "futures-timer 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", "sr-version 2.0.0", "srml-aura 2.0.0", "srml-support 2.0.0", + "substrate-application-crypto 2.0.0", "substrate-client 2.0.0", "substrate-consensus-aura-primitives 2.0.0", "substrate-consensus-common 2.0.0", @@ -4355,6 +4409,7 @@ dependencies = [ "substrate-executor 2.0.0", "substrate-inherents 2.0.0", "substrate-keyring 2.0.0", + "substrate-keystore 2.0.0", "substrate-network 2.0.0", "substrate-primitives 2.0.0", "substrate-service 2.0.0", @@ -4370,8 +4425,8 @@ dependencies = [ "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "sr-std 2.0.0", + "substrate-application-crypto 2.0.0", "substrate-client 2.0.0", - "substrate-primitives 2.0.0", ] [[package]] @@ -4389,7 +4444,7 @@ dependencies = [ "num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "schnorrkel 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", @@ -4397,6 +4452,7 @@ dependencies = [ "sr-version 2.0.0", "srml-babe 2.0.0", "srml-support 2.0.0", + "substrate-application-crypto 2.0.0", "substrate-client 2.0.0", "substrate-consensus-babe-primitives 2.0.0", "substrate-consensus-common 2.0.0", @@ -4404,11 +4460,13 @@ dependencies = [ "substrate-executor 2.0.0", "substrate-inherents 2.0.0", "substrate-keyring 2.0.0", + "substrate-keystore 2.0.0", "substrate-network 2.0.0", "substrate-primitives 2.0.0", "substrate-service 2.0.0", "substrate-telemetry 2.0.0", "substrate-test-runtime-client 2.0.0", + "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -4416,13 +4474,13 @@ dependencies = [ name = "substrate-consensus-babe-primitives" version = "2.0.0" dependencies = [ - "schnorrkel 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "schnorrkel 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "sr-std 2.0.0", + "substrate-application-crypto 2.0.0", "substrate-client 2.0.0", "substrate-consensus-slots 2.0.0", - "substrate-primitives 2.0.0", ] [[package]] @@ -4435,7 +4493,7 @@ dependencies = [ "libp2p 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "sr-std 2.0.0", "sr-version 2.0.0", @@ -4463,7 +4521,7 @@ dependencies = [ "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "rhododendron 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", @@ -4486,7 +4544,7 @@ dependencies = [ "futures-timer 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-client 2.0.0", "substrate-consensus-common 2.0.0", @@ -4508,7 +4566,7 @@ dependencies = [ "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "parity-wasm 0.31.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-version 2.0.0", "substrate-client 2.0.0", @@ -4535,21 +4593,24 @@ dependencies = [ "futures-preview 0.3.0-alpha.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "srml-finality-tracker 2.0.0", "substrate-client 2.0.0", + "substrate-consensus-babe-primitives 2.0.0", "substrate-consensus-common 2.0.0", "substrate-finality-grandpa-primitives 2.0.0", "substrate-inherents 2.0.0", "substrate-keyring 2.0.0", + "substrate-keystore 2.0.0", "substrate-network 2.0.0", "substrate-primitives 2.0.0", "substrate-service 2.0.0", "substrate-telemetry 2.0.0", "substrate-test-runtime-client 2.0.0", + "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4563,8 +4624,8 @@ dependencies = [ "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "sr-std 2.0.0", + "substrate-application-crypto 2.0.0", "substrate-client 2.0.0", - "substrate-primitives 2.0.0", ] [[package]] @@ -4572,7 +4633,7 @@ name = "substrate-inherents" version = "2.0.0" dependencies = [ "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "sr-std 2.0.0", ] @@ -4592,10 +4653,12 @@ dependencies = [ name = "substrate-keystore" version = "2.0.0" dependencies = [ - "derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "substrate-application-crypto 2.0.0", "substrate-primitives 2.0.0", "subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4622,7 +4685,7 @@ dependencies = [ "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "quickcheck 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4633,6 +4696,7 @@ dependencies = [ "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-client 2.0.0", + "substrate-consensus-babe-primitives 2.0.0", "substrate-consensus-common 2.0.0", "substrate-keyring 2.0.0", "substrate-peerset 2.0.0", @@ -4656,10 +4720,11 @@ dependencies = [ "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-client 2.0.0", "substrate-client-db 2.0.0", + "substrate-keystore 2.0.0", "substrate-network 2.0.0", "substrate-offchain-primitives 2.0.0", "substrate-primitives 2.0.0", @@ -4714,7 +4779,7 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "primitive-types 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4746,7 +4811,7 @@ dependencies = [ "jsonrpc-pubsub 12.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (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.97 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4755,6 +4820,7 @@ dependencies = [ "sr-version 2.0.0", "substrate-client 2.0.0", "substrate-executor 2.0.0", + "substrate-keystore 2.0.0", "substrate-network 2.0.0", "substrate-primitives 2.0.0", "substrate-state-machine 2.0.0", @@ -4810,22 +4876,26 @@ dependencies = [ "node-runtime 2.0.0", "parity-multiaddr 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "sr-io 2.0.0", "sr-primitives 2.0.0", + "substrate-application-crypto 2.0.0", "substrate-client 2.0.0", "substrate-client-db 2.0.0", + "substrate-consensus-babe-primitives 2.0.0", "substrate-consensus-common 2.0.0", "substrate-executor 2.0.0", "substrate-finality-grandpa 2.0.0", + "substrate-finality-grandpa-primitives 2.0.0", "substrate-keystore 2.0.0", "substrate-network 2.0.0", "substrate-offchain 2.0.0", "substrate-primitives 2.0.0", "substrate-rpc-servers 2.0.0", + "substrate-session 2.0.0", "substrate-telemetry 2.0.0", "substrate-test-runtime-client 2.0.0", "substrate-transaction-pool 2.0.0", @@ -4853,6 +4923,16 @@ dependencies = [ "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "substrate-session" +version = "2.0.0" +dependencies = [ + "sr-primitives 2.0.0", + "sr-std 2.0.0", + "substrate-client 2.0.0", + "substrate-primitives 2.0.0", +] + [[package]] name = "substrate-state-db" version = "2.0.0" @@ -4860,7 +4940,7 @@ dependencies = [ "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-primitives 2.0.0", ] @@ -4873,7 +4953,7 @@ dependencies = [ "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-panic-handler 2.0.0", "substrate-primitives 2.0.0", "substrate-trie 2.0.0", @@ -4891,7 +4971,7 @@ dependencies = [ "futures-timer 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "libp2p 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", "slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4937,6 +5017,7 @@ dependencies = [ "srml-support 2.0.0", "srml-system 2.0.0", "srml-timestamp 2.0.0", + "substrate-application-crypto 2.0.0", "substrate-client 2.0.0", "substrate-consensus-aura-primitives 2.0.0", "substrate-consensus-babe-primitives 2.0.0", @@ -4972,7 +5053,7 @@ dependencies = [ "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-primitives 2.0.0", @@ -4987,7 +5068,7 @@ dependencies = [ "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "sr-primitives 2.0.0", "substrate-client 2.0.0", "substrate-keyring 2.0.0", @@ -6075,6 +6156,7 @@ dependencies = [ "checksum curve25519-dalek 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5d4b820e8711c211745880150f5fac78ab07d6e3851d8ce9f5a02cedc199174c" "checksum data-encoding 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97" "checksum derive_more 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6d944ac6003ed268757ef1ee686753b57efc5fcf0ebe7b64c9fc81e7e32ff839" +"checksum derive_more 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a141330240c921ec6d074a3e188a7c7ef95668bb95e7d44fa0e5778ec2a7afe" "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" "checksum digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" diff --git a/Cargo.toml b/Cargo.toml index a6a7b8d17ba35..298882e9a694a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ vergen = "3" [workspace] members = [ + "core/application-crypto", "core/cli", "core/client", "core/client/db", @@ -42,6 +43,7 @@ members = [ "core/serializer", "core/service", "core/service/test", + "core/session", "core/sr-api-macros", "core/sr-io", "core/sr-primitives", @@ -79,6 +81,7 @@ members = [ "srml/grandpa", "srml/im-online", "srml/indices", + "srml/membership", "srml/metadata", "srml/session", "srml/staking", diff --git a/core/application-crypto/Cargo.toml b/core/application-crypto/Cargo.toml new file mode 100644 index 0000000000000..6d39b12653fa5 --- /dev/null +++ b/core/application-crypto/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "substrate-application-crypto" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" +description = "Provides facilities for generating application specific crypto wrapper types." + +[dependencies] +primitives = { package = "substrate-primitives", path = "../primitives", default-features = false } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true, features = ["derive"] } +rstd = { package = "sr-std", path = "../sr-std", default-features = false } +rio = { package = "sr-io", path = "../sr-io", default-features = false } + +[dev-dependencies] +test-client = { package = "substrate-test-runtime-client", path = "../test-runtime/client" } +sr-primitives = { path = "../sr-primitives" } + +[features] +default = [ "std" ] +std = [ "primitives/std", "codec/std", "serde", "rstd/std", "rio/std" ] diff --git a/core/application-crypto/src/ed25519.rs b/core/application-crypto/src/ed25519.rs new file mode 100644 index 0000000000000..6c5458492b6b8 --- /dev/null +++ b/core/application-crypto/src/ed25519.rs @@ -0,0 +1,75 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Ed25519 crypto types. + +use crate::{RuntimePublic, KeyTypeId}; + +pub use primitives::ed25519::*; + +mod app { + use crate::key_types::ED25519; + crate::app_crypto!(super, ED25519); +} + +pub use app::Public as AppPublic; +pub use app::Signature as AppSignature; +#[cfg(feature="std")] +pub use app::Pair as AppPair; + +impl RuntimePublic for Public { + type Signature = Signature; + + fn all(key_type: KeyTypeId) -> crate::Vec { + rio::ed25519_public_keys(key_type) + } + + fn generate_pair(key_type: KeyTypeId, seed: Option<&str>) -> Self { + rio::ed25519_generate(key_type, seed) + } + + fn sign>(&self, key_type: KeyTypeId, msg: &M) -> Option { + rio::ed25519_sign(key_type, self, msg) + } + + fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool { + rio::ed25519_verify(&signature, msg.as_ref(), self) + } +} + +#[cfg(test)] +mod tests { + use sr_primitives::{generic::BlockId, traits::ProvideRuntimeApi}; + use primitives::{testing::KeyStore, crypto::Pair, traits::BareCryptoStore as _}; + use test_client::{ + TestClientBuilder, DefaultTestClientBuilderExt, TestClientBuilderExt, + runtime::{TestAPI, app_crypto::ed25519::{AppPair, AppPublic}}, + }; + + #[test] + fn ed25519_works_in_runtime() { + let keystore = KeyStore::new(); + let test_client = TestClientBuilder::new().set_keystore(keystore.clone()).build(); + let (signature, public) = test_client.runtime_api() + .test_ed25519_crypto(&BlockId::Number(0)) + .expect("Tests `ed25519` crypto."); + + let key_pair = keystore.read().ed25519_key_pair(crate::key_types::ED25519, &public.as_ref()) + .expect("There should be at a `ed25519` key in the keystore for the given public key."); + + assert!(AppPair::verify(&signature, "ed25519", &AppPublic::from(key_pair.public()))); + } +} diff --git a/core/application-crypto/src/lib.rs b/core/application-crypto/src/lib.rs new file mode 100644 index 0000000000000..d9bff822eedee --- /dev/null +++ b/core/application-crypto/src/lib.rs @@ -0,0 +1,320 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Traits and macros for constructing application specific strongly typed crypto wrappers. + +#![warn(missing_docs)] + +#![cfg_attr(not(feature = "std"), no_std)] + +#[doc(hidden)] +pub use primitives::{self, crypto::{CryptoType, Public, Derive, IsWrappedBy, Wraps}}; +#[doc(hidden)] +#[cfg(feature = "std")] +pub use primitives::crypto::{SecretStringError, DeriveJunction, Ss58Codec, Pair}; +pub use primitives::{crypto::{KeyTypeId, key_types}}; + +#[doc(hidden)] +pub use codec; +#[doc(hidden)] +#[cfg(feature = "std")] +pub use serde; +#[doc(hidden)] +pub use rstd::{ops::Deref, vec::Vec}; + +pub mod ed25519; +pub mod sr25519; +mod traits; + +pub use traits::*; + +/// Declares Public, Pair, Signature types which are functionally equivalent to `$pair`, but are new +/// Application-specific types whose identifier is `$key_type`. +/// +/// ```rust +///# use substrate_application_crypto::{app_crypto, wrap, ed25519, KeyTypeId}; +/// // Declare a new set of crypto types using Ed25519 logic that identifies as `KeyTypeId` +/// // of value `b"fuba"`. +/// app_crypto!(ed25519, KeyTypeId(*b"_uba")); +/// ``` +#[macro_export] +macro_rules! app_crypto { + ($module:ident, $key_type:expr) => { + #[cfg(feature="std")] + $crate::app_crypto!($module::Pair, $module::Public, $module::Signature, $key_type); + #[cfg(not(feature="std"))] + $crate::app_crypto!($module::Public, $module::Signature, $key_type); + }; + ($pair:ty, $public:ty, $sig:ty, $key_type:expr) => { + $crate::app_crypto!($public, $sig, $key_type); + + $crate::wrap!{ + /// A generic `AppPublic` wrapper type over $pair crypto; this has no specific App. + #[derive(Clone)] + pub struct Pair($pair); + } + + impl $crate::CryptoType for Pair { + type Pair = Pair; + } + + #[cfg(feature = "std")] + impl $crate::Pair for Pair { + type Public = Public; + type Seed = <$pair as $crate::Pair>::Seed; + type Signature = Signature; + type DeriveError = <$pair as $crate::Pair>::DeriveError; + fn generate_with_phrase(password: Option<&str>) -> (Self, String, Self::Seed) { + let r = <$pair>::generate_with_phrase(password); + (Self(r.0), r.1, r.2) + } + fn from_phrase(phrase: &str, password: Option<&str>) + -> Result<(Self, Self::Seed), $crate::SecretStringError> + { + <$pair>::from_phrase(phrase, password).map(|r| (Self(r.0), r.1)) + } + fn derive< + Iter: Iterator + >(&self, path: Iter) -> Result { + self.0.derive(path).map(Self) + } + fn from_seed(seed: &Self::Seed) -> Self { Self(<$pair>::from_seed(seed)) } + fn from_seed_slice(seed: &[u8]) -> Result { + <$pair>::from_seed_slice(seed).map(Self) + } + fn from_standard_components< + I: Iterator + >( + seed: &str, + password: Option<&str>, + path: I, + ) -> Result { + <$pair>::from_standard_components::(seed, password, path).map(Self) + } + fn sign(&self, msg: &[u8]) -> Self::Signature { + Signature(self.0.sign(msg)) + } + fn verify>( + sig: &Self::Signature, + message: M, + pubkey: &Self::Public, + ) -> bool { + <$pair>::verify(&sig.0, message, pubkey.as_ref()) + } + fn verify_weak, M: AsRef<[u8]>>( + sig: &[u8], + message: M, + pubkey: P, + ) -> bool { + <$pair>::verify_weak(sig, message, pubkey) + } + fn public(&self) -> Self::Public { Public(self.0.public()) } + fn to_raw_vec(&self) -> Vec { self.0.to_raw_vec() } + } + impl $crate::AppKey for Pair { + type UntypedGeneric = $pair; + type Public = Public; + type Pair = Pair; + type Signature = Signature; + const ID: $crate::KeyTypeId = $key_type; + } + impl $crate::AppPair for Pair { + type Generic = $pair; + } + }; + ($public:ty, $sig:ty, $key_type:expr) => { + $crate::wrap!{ + /// A generic `AppPublic` wrapper type over $public crypto; this has no specific App. + #[derive( + Clone, Default, Eq, PartialEq, Ord, PartialOrd, $crate::codec::Encode, + $crate::codec::Decode, + )] + #[cfg_attr(feature = "std", derive(Debug, Hash))] + pub struct Public($public); + } + + impl $crate::Derive for Public { + #[cfg(feature = "std")] + fn derive>(&self, + path: Iter + ) -> Option { + self.0.derive(path).map(Self) + } + } + + #[cfg(feature = "std")] + impl std::fmt::Display for Public { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + use $crate::Ss58Codec; + write!(f, "{}", self.0.to_ss58check()) + } + } + #[cfg(feature = "std")] + impl $crate::serde::Serialize for Public { + fn serialize(&self, serializer: S) -> std::result::Result where + S: $crate::serde::Serializer + { + use $crate::Ss58Codec; + serializer.serialize_str(&self.to_ss58check()) + } + } + #[cfg(feature = "std")] + impl<'de> $crate::serde::Deserialize<'de> for Public { + fn deserialize(deserializer: D) -> std::result::Result where + D: $crate::serde::Deserializer<'de> + { + use $crate::Ss58Codec; + Public::from_ss58check(&String::deserialize(deserializer)?) + .map_err(|e| $crate::serde::de::Error::custom(format!("{:?}", e))) + } + } + + impl AsRef<[u8]> for Public { + fn as_ref(&self) -> &[u8] { self.0.as_ref() } + } + + impl AsMut<[u8]> for Public { + fn as_mut(&mut self) -> &mut [u8] { self.0.as_mut() } + } + + impl $crate::CryptoType for Public { + #[cfg(feature="std")] + type Pair = Pair; + } + + impl $crate::Public for Public { + fn from_slice(x: &[u8]) -> Self { Self(<$public>::from_slice(x)) } + } + + impl $crate::AppKey for Public { + type UntypedGeneric = $public; + type Public = Public; + #[cfg(feature="std")] + type Pair = Pair; + type Signature = Signature; + const ID: $crate::KeyTypeId = $key_type; + } + + impl $crate::AppPublic for Public { + type Generic = $public; + } + + impl $crate::RuntimeAppPublic for Public where $public: $crate::RuntimePublic { + type Signature = Signature; + + fn all() -> $crate::Vec { + <$public as $crate::RuntimePublic>::all($key_type).into_iter().map(Self).collect() + } + + fn generate_pair(seed: Option<&str>) -> Self { + Self(<$public as $crate::RuntimePublic>::generate_pair($key_type, seed)) + } + + fn sign>(&self, msg: &M) -> Option { + <$public as $crate::RuntimePublic>::sign( + self.as_ref(), + $key_type, + msg, + ).map(Signature) + } + + fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool { + <$public as $crate::RuntimePublic>::verify(self.as_ref(), msg, &signature.as_ref()) + } + } + + $crate::wrap! { + /// A generic `AppPublic` wrapper type over $public crypto; this has no specific App. + #[derive(Clone, Default, Eq, PartialEq, $crate::codec::Encode, $crate::codec::Decode)] + #[cfg_attr(feature = "std", derive(Debug, Hash))] + pub struct Signature($sig); + } + + impl $crate::Deref for Signature { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { self.0.as_ref() } + } + + impl AsRef<[u8]> for Signature { + fn as_ref(&self) -> &[u8] { self.0.as_ref() } + } + + impl $crate::CryptoType for Signature { + #[cfg(feature="std")] + type Pair = Pair; + } + + impl $crate::AppKey for Signature { + type UntypedGeneric = $sig; + type Public = Public; + #[cfg(feature="std")] + type Pair = Pair; + type Signature = Signature; + const ID: $crate::KeyTypeId = $key_type; + } + + impl $crate::AppSignature for Signature { + type Generic = $sig; + } + } +} + +/// Implement bidirectional `From` and on-way `AsRef`/`AsMut` for two types, `$inner` and `$outer`. +/// +/// ```rust +/// substrate_application_crypto::wrap! { +/// pub struct Wrapper(u32); +/// } +/// ``` +#[macro_export] +macro_rules! wrap { + ($( #[ $attr:meta ] )* struct $outer:ident($inner:ty);) => { + $( #[ $attr ] )* + struct $outer( $inner ); + $crate::wrap!($inner, $outer); + }; + ($( #[ $attr:meta ] )* pub struct $outer:ident($inner:ty);) => { + $( #[ $attr ] )* + pub struct $outer( $inner ); + $crate::wrap!($inner, $outer); + }; + ($inner:ty, $outer:ty) => { + impl $crate::Wraps for $outer { + type Inner = $inner; + } + impl From<$inner> for $outer { + fn from(inner: $inner) -> Self { + Self(inner) + } + } + impl From<$outer> for $inner { + fn from(outer: $outer) -> Self { + outer.0 + } + } + impl AsRef<$inner> for $outer { + fn as_ref(&self) -> &$inner { + &self.0 + } + } + impl AsMut<$inner> for $outer { + fn as_mut(&mut self) -> &mut $inner { + &mut self.0 + } + } + } +} diff --git a/core/application-crypto/src/sr25519.rs b/core/application-crypto/src/sr25519.rs new file mode 100644 index 0000000000000..af112dc70ee7e --- /dev/null +++ b/core/application-crypto/src/sr25519.rs @@ -0,0 +1,75 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Sr25519 crypto types. + +use crate::{RuntimePublic, KeyTypeId}; + +pub use primitives::sr25519::*; + +mod app { + use crate::key_types::SR25519; + crate::app_crypto!(super, SR25519); +} + +pub use app::Public as AppPublic; +pub use app::Signature as AppSignature; +#[cfg(feature="std")] +pub use app::Pair as AppPair; + +impl RuntimePublic for Public { + type Signature = Signature; + + fn all(key_type: KeyTypeId) -> crate::Vec { + rio::sr25519_public_keys(key_type) + } + + fn generate_pair(key_type: KeyTypeId, seed: Option<&str>) -> Self { + rio::sr25519_generate(key_type, seed) + } + + fn sign>(&self, key_type: KeyTypeId, msg: &M) -> Option { + rio::sr25519_sign(key_type, self, msg) + } + + fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool { + rio::sr25519_verify(&signature, msg.as_ref(), self) + } +} + +#[cfg(test)] +mod tests { + use sr_primitives::{generic::BlockId, traits::ProvideRuntimeApi}; + use primitives::{testing::KeyStore, crypto::Pair, traits::BareCryptoStore as _}; + use test_client::{ + TestClientBuilder, DefaultTestClientBuilderExt, TestClientBuilderExt, + runtime::{TestAPI, app_crypto::sr25519::{AppPair, AppPublic}}, + }; + + #[test] + fn sr25519_works_in_runtime() { + let keystore = KeyStore::new(); + let test_client = TestClientBuilder::new().set_keystore(keystore.clone()).build(); + let (signature, public) = test_client.runtime_api() + .test_sr25519_crypto(&BlockId::Number(0)) + .expect("Tests `sr25519` crypto."); + + let key_pair = keystore.read().sr25519_key_pair(crate::key_types::SR25519, public.as_ref()) + .expect("There should be at a `sr25519` key in the keystore for the given public key."); + + assert!(AppPair::verify(&signature, "sr25519", &AppPublic::from(key_pair.public()))); + } +} diff --git a/core/application-crypto/src/traits.rs b/core/application-crypto/src/traits.rs new file mode 100644 index 0000000000000..d7f1eafe35407 --- /dev/null +++ b/core/application-crypto/src/traits.rs @@ -0,0 +1,120 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use primitives::crypto::{KeyTypeId, CryptoType, IsWrappedBy, Public}; +#[cfg(feature = "std")] +use primitives::crypto::Pair; + +/// An application-specific key. +pub trait AppKey: 'static + Send + Sync + Sized + CryptoType + Clone { + /// The corresponding type as a generic crypto type. + type UntypedGeneric: IsWrappedBy; + + /// The corresponding public key type in this application scheme. + type Public: AppPublic; + + /// The corresponding key pair type in this application scheme. + #[cfg(feature="std")] + type Pair: AppPair; + + /// The corresponding signature type in this application scheme. + type Signature: AppSignature; + + /// An identifier for this application-specific key type. + const ID: KeyTypeId; +} + +/// Type which implements Debug and Hash in std, not when no-std (std variant). +#[cfg(feature = "std")] +pub trait MaybeDebugHash: std::fmt::Debug + std::hash::Hash {} +#[cfg(feature = "std")] +impl MaybeDebugHash for T {} + +/// Type which implements Debug and Hash in std, not when no-std (no-std variant). +#[cfg(not(feature = "std"))] +pub trait MaybeDebugHash {} +#[cfg(not(feature = "std"))] +impl MaybeDebugHash for T {} + +/// A application's public key. +pub trait AppPublic: AppKey + Public + Ord + PartialOrd + Eq + PartialEq + MaybeDebugHash + codec::Codec { + /// The wrapped type which is just a plain instance of `Public`. + type Generic: + IsWrappedBy + Public + Ord + PartialOrd + Eq + PartialEq + MaybeDebugHash + codec::Codec; +} + +/// A application's key pair. +#[cfg(feature = "std")] +pub trait AppPair: AppKey + Pair::Public> { + /// The wrapped type which is just a plain instance of `Pair`. + type Generic: IsWrappedBy + Pair::Public as AppPublic>::Generic>; +} + +/// A application's signature. +pub trait AppSignature: AppKey + Eq + PartialEq + MaybeDebugHash { + /// The wrapped type which is just a plain instance of `Signature`. + type Generic: IsWrappedBy + Eq + PartialEq + MaybeDebugHash; +} + +/// A runtime interface for a public key. +pub trait RuntimePublic: Sized { + /// The signature that will be generated when signing with the corresponding private key. + type Signature; + + /// Returns all public keys for the given key type in the keystore. + fn all(key_type: KeyTypeId) -> crate::Vec; + + /// Generate a public/private pair for the given key type and store it in the keystore. + /// + /// Returns the generated public key. + fn generate_pair(key_type: KeyTypeId, seed: Option<&str>) -> Self; + + /// Sign the given message with the corresponding private key of this public key. + /// + /// The private key will be requested from the keystore using the given key type. + /// + /// Returns the signature or `None` if the private key could not be found or some other error + /// occurred. + fn sign>(&self, key_type: KeyTypeId, msg: &M) -> Option; + + /// Verify that the given signature matches the given message using this public key. + fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool; +} + +/// A runtime interface for an application's public key. +pub trait RuntimeAppPublic: Sized { + /// The signature that will be generated when signing with the corresponding private key. + type Signature; + + /// Returns all public keys for this application in the keystore. + fn all() -> crate::Vec; + + /// Generate a public/private pair and store it in the keystore. + /// + /// Returns the generated public key. + fn generate_pair(seed: Option<&str>) -> Self; + + /// Sign the given message with the corresponding private key of this public key. + /// + /// The private key will be requested from the keystore. + /// + /// Returns the signature or `None` if the private key could not be found or some other error + /// occurred. + fn sign>(&self, msg: &M) -> Option; + + /// Verify that the given signature matches the given message using this public key. + fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool; +} diff --git a/core/cli/src/lib.rs b/core/cli/src/lib.rs index eb0e443a57491..0f6769a699025 100644 --- a/core/cli/src/lib.rs +++ b/core/cli/src/lib.rs @@ -366,6 +366,24 @@ fn input_keystore_password() -> Result { .map_err(|e| format!("{:?}", e)) } +/// Fill the password field of the given config instance. +fn fill_config_keystore_password( + config: &mut service::Configuration, + cli: &RunCmd, +) -> Result<(), String> { + config.keystore_password = if cli.password_interactive { + Some(input_keystore_password()?.into()) + } else if let Some(ref file) = cli.password_filename { + Some(fs::read_to_string(file).map_err(|e| format!("{}", e))?.into()) + } else if let Some(ref password) = cli.password { + Some(password.clone().into()) + } else { + None + }; + + Ok(()) +} + fn create_run_node_config( cli: RunCmd, spec_factory: S, impl_name: &'static str, version: &VersionInfo ) -> error::Result> @@ -375,9 +393,8 @@ where { let spec = load_spec(&cli.shared_params, spec_factory)?; let mut config = service::Configuration::default_with_spec(spec.clone()); - if cli.interactive_password { - config.password = input_keystore_password()?.into() - } + + fill_config_keystore_password(&mut config, &cli)?; config.impl_name = impl_name; config.impl_commit = version.commit; @@ -401,7 +418,9 @@ where let base_path = base_path(&cli.shared_params, version); - config.keystore_path = cli.keystore_path.or_else(|| Some(keystore_path(&base_path, config.chain_spec.id()))); + config.keystore_path = cli.keystore_path.unwrap_or_else( + || keystore_path(&base_path, config.chain_spec.id()) + ); config.database_path = db_path(&base_path, config.chain_spec.id()); config.database_cache_size = cli.database_cache_size; @@ -462,17 +481,13 @@ where cli.pool_config, )?; - if let Some(key) = cli.key { - config.keys.push(key); - } - if cli.shared_params.dev && cli.keyring.account.is_none() { - config.keys.push("//Alice".into()); + if cli.shared_params.dev { + config.dev_key_seed = cli.keyring.account + .map(|a| format!("//{}", a)) + .or_else(|| Some("//Alice".into())); } - if let Some(account) = cli.keyring.account { - config.keys.push(format!("//{}", account)); - } let rpc_interface: &str = if cli.rpc_external { "0.0.0.0" } else { "127.0.0.1" }; let ws_interface: &str = if cli.ws_external { "0.0.0.0" } else { "127.0.0.1" }; diff --git a/core/cli/src/params.rs b/core/cli/src/params.rs index e1018eb93a4e8..6f92e8c9b4de8 100644 --- a/core/cli/src/params.rs +++ b/core/cli/src/params.rs @@ -36,9 +36,13 @@ arg_enum! { #[allow(missing_docs)] #[derive(Debug, Clone, Copy)] pub enum ExecutionStrategy { + // Execute with native build (if available, WebAssembly otherwise). Native, + // Only execute with the WebAssembly build. Wasm, + // Execute with both native (where available) and WebAssembly builds. Both, + // Execute with the native build if possible; if it fails, then execute with WebAssembly. NativeElseWasm, } } @@ -55,7 +59,8 @@ impl Into for ExecutionStrategy { } arg_enum! { - /// How to execute blocks + /// Whether off-chain workers are enabled. + #[allow(missing_docs)] #[derive(Debug, Clone)] pub enum OffchainWorkerEnabled { Always, @@ -306,14 +311,6 @@ pub struct ExecutionStrategies { /// The `run` command used to run a node. #[derive(Debug, StructOpt, Clone)] pub struct RunCmd { - /// Specify custom keystore path - #[structopt(long = "keystore-path", value_name = "PATH", parse(from_os_str))] - pub keystore_path: Option, - - /// Specify additional key seed - #[structopt(long = "key", value_name = "STRING")] - pub key: Option, - /// Enable validator mode #[structopt(long = "validator")] pub validator: bool, @@ -422,9 +419,32 @@ pub struct RunCmd { #[structopt(long = "force-authoring")] pub force_authoring: bool, - /// Interactive password for validator key. - #[structopt(short = "i")] - pub interactive_password: bool, + /// Specify custom keystore path. + #[structopt(long = "keystore-path", value_name = "PATH", parse(from_os_str))] + pub keystore_path: Option, + + /// Use interactive shell for entering the password used by the keystore. + #[structopt( + long = "password-interactive", + raw(conflicts_with_all = "&[ \"password\", \"password_filename\" ]") + )] + pub password_interactive: bool, + + /// Password used by the keystore. + #[structopt( + long = "password", + raw(conflicts_with_all = "&[ \"password_interactive\", \"password_filename\" ]") + )] + pub password: Option, + + /// File that contains the password used by the keystore. + #[structopt( + long = "password-filename", + value_name = "PATH", + parse(from_os_str), + raw(conflicts_with_all = "&[ \"password_interactive\", \"password\" ]") + )] + pub password_filename: Option } /// Stores all required Cli values for a keyring test account. @@ -443,7 +463,7 @@ lazy_static::lazy_static! { let conflicts_with = keyring::Sr25519Keyring::iter() .filter(|b| a != *b) .map(|b| b.to_string().to_lowercase()) - .chain(["name", "key"].iter().map(ToString::to_string)) + .chain(std::iter::once("name".to_string())) .collect::>(); let name = a.to_string().to_lowercase(); @@ -485,6 +505,7 @@ impl AugmentClap for Keyring { .long(&a.name) .help(&a.help) .conflicts_with_all(&conflicts_with_strs) + .requires("dev") .takes_value(false) ) }) diff --git a/core/client/Cargo.toml b/core/client/Cargo.toml index 5848c67349852..793db376135fc 100644 --- a/core/client/Cargo.toml +++ b/core/client/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" derive_more = { version = "0.14.0", optional = true } fnv = { version = "1.0", optional = true } log = { version = "0.4", optional = true } -parking_lot = { version = "0.8.0", optional = true } +parking_lot = { version = "0.9.0", optional = true } hex = { package = "hex-literal", version = "0.2", optional = true } futures-preview = { version = "0.3.0-alpha.17", optional = true } consensus = { package = "substrate-consensus-common", path = "../consensus/common", optional = true } diff --git a/core/client/db/Cargo.toml b/core/client/db/Cargo.toml index 5df825fe12747..b7bc835ef012a 100644 --- a/core/client/db/Cargo.toml +++ b/core/client/db/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -parking_lot = "0.8" +parking_lot = "0.9.0" log = "0.4" kvdb = { git = "https://github.com/paritytech/parity-common", rev="b0317f649ab2c665b7987b8475878fc4d2e1f81d" } # FIXME replace with release as soon as our rocksdb changes are released upstream https://github.com/paritytech/parity-common/issues/88 diff --git a/core/client/db/src/lib.rs b/core/client/db/src/lib.rs index a25ded0db3246..250af8ea60582 100644 --- a/core/client/db/src/lib.rs +++ b/core/client/db/src/lib.rs @@ -195,6 +195,7 @@ pub fn new_client( executor: E, genesis_storage: S, execution_strategies: ExecutionStrategies, + keystore: Option, ) -> Result< client::Client, client::LocalCallExecutor, E>, Block, RA>, client::error::Error @@ -205,7 +206,7 @@ pub fn new_client( S: BuildStorage, { let backend = Arc::new(Backend::new(settings, CANONICALIZATION_DELAY)?); - let executor = client::LocalCallExecutor::new(backend.clone(), executor); + let executor = client::LocalCallExecutor::new(backend.clone(), executor, keystore); Ok(client::Client::new(backend, executor, genesis_storage, execution_strategies)?) } diff --git a/core/client/src/call_executor.rs b/core/client/src/call_executor.rs index f25f77c484ecc..e82bd9a22c91e 100644 --- a/core/client/src/call_executor.rs +++ b/core/client/src/call_executor.rs @@ -83,6 +83,7 @@ where native_call: Option, side_effects_handler: Option<&mut O>, proof_recorder: &Option>>>, + enable_keystore: bool, ) -> error::Result> where ExecutionManager: Clone; /// Extract RuntimeVersion of given block @@ -150,14 +151,20 @@ where pub struct LocalCallExecutor { backend: Arc, executor: E, + keystore: Option, } impl LocalCallExecutor { /// Creates new instance of local call executor. - pub fn new(backend: Arc, executor: E) -> Self { + pub fn new( + backend: Arc, + executor: E, + keystore: Option, + ) -> Self { LocalCallExecutor { backend, executor, + keystore, } } } @@ -167,6 +174,7 @@ impl Clone for LocalCallExecutor where E: Clone { LocalCallExecutor { backend: self.backend.clone(), executor: self.executor.clone(), + keystore: self.keystore.clone(), } } } @@ -197,6 +205,7 @@ where &self.executor, method, call_data, + self.keystore.clone(), ).execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( strategy.get_manager(), false, @@ -229,6 +238,7 @@ where native_call: Option, side_effects_handler: Option<&mut O>, recorder: &Option>>>, + enable_keystore: bool, ) -> Result, error::Error> where ExecutionManager: Clone { match initialize_block { InitializeBlock::Do(ref init_block) @@ -239,6 +249,12 @@ where _ => {}, } + let keystore = if enable_keystore { + self.keystore.clone() + } else { + None + }; + let mut state = self.backend.state_at(*at)?; let result = match recorder { @@ -262,6 +278,7 @@ where &self.executor, method, call_data, + keystore, ) .execute_using_consensus_failure_handler( execution_manager, @@ -279,6 +296,7 @@ where &self.executor, method, call_data, + keystore, ) .execute_using_consensus_failure_handler( execution_manager, @@ -294,7 +312,14 @@ where fn runtime_version(&self, id: &BlockId) -> error::Result { let mut overlay = OverlayedChanges::default(); let state = self.backend.state_at(*id)?; - let mut ext = Ext::new(&mut overlay, &state, self.backend.changes_trie_storage(), NeverOffchainExt::new()); + + let mut ext = Ext::new( + &mut overlay, + &state, + self.backend.changes_trie_storage(), + NeverOffchainExt::new(), + None, + ); let version = self.executor.runtime_version(&mut ext); self.backend.destroy_state(state)?; version.ok_or(error::Error::VersionInvalid.into()) @@ -330,6 +355,7 @@ where &self.executor, method, call_data, + self.keystore.clone(), ).execute_using_consensus_failure_handler( manager, true, @@ -356,6 +382,7 @@ where &self.executor, method, call_data, + self.keystore.clone(), ) .map_err(Into::into) } diff --git a/core/client/src/client.rs b/core/client/src/client.rs index cd6c2d63b79f7..7213db0c9fd29 100644 --- a/core/client/src/client.rs +++ b/core/client/src/client.rs @@ -20,66 +20,59 @@ use std::{ marker::PhantomData, collections::{HashSet, BTreeMap, HashMap}, sync::Arc, panic::UnwindSafe, result, cell::RefCell, rc::Rc, }; -use crate::error::Error; +use log::{info, trace, warn}; use futures::channel::mpsc; use parking_lot::{Mutex, RwLock}; -use primitives::NativeOrEncoded; -use sr_primitives::{ - Justification, - generic::{BlockId, SignedBlock}, -}; -use consensus::{ - Error as ConsensusError, BlockImportParams, - ImportResult, BlockOrigin, ForkChoiceStrategy, - well_known_cache_keys::Id as CacheKeyId, - SelectChain, self, -}; -use sr_primitives::traits::{ - Block as BlockT, Header as HeaderT, Zero, NumberFor, - ApiRef, ProvideRuntimeApi, SaturatedConversion, One, DigestFor, -}; -use sr_primitives::generic::DigestItem; -use sr_primitives::BuildStorage; -use crate::runtime_api::{ - CallRuntimeAt, ConstructRuntimeApi, Core as CoreApi, ProofRecorder, - InitializeBlock, -}; +use codec::{Encode, Decode}; +use hash_db::{Hasher, Prefix}; use primitives::{ Blake2Hasher, H256, ChangesTrieConfiguration, convert_hash, - NeverNativeValue, ExecutionContext + NeverNativeValue, ExecutionContext, + storage::{StorageKey, StorageData, well_known_keys}, NativeOrEncoded +}; +use substrate_telemetry::{telemetry, SUBSTRATE_INFO}; +use sr_primitives::{ + Justification, BuildStorage, + generic::{BlockId, SignedBlock, DigestItem}, + traits::{ + Block as BlockT, Header as HeaderT, Zero, NumberFor, + ApiRef, ProvideRuntimeApi, SaturatedConversion, One, DigestFor, + }, }; -use primitives::storage::{StorageKey, StorageData}; -use primitives::storage::well_known_keys; -use codec::{Encode, Decode}; use state_machine::{ DBValue, Backend as StateBackend, CodeExecutor, ChangesTrieAnchorBlockId, ExecutionStrategy, ExecutionManager, prove_read, prove_child_read, ChangesTrieRootsStorage, ChangesTrieStorage, key_changes, key_changes_proof, OverlayedChanges, NeverOffchainExt, }; -use hash_db::{Hasher, Prefix}; - -use crate::backend::{ - self, BlockImportOperation, PrunableStateChangesTrieStorage, - StorageCollection, ChildStorageCollection -}; -use crate::blockchain::{ - self, Info as ChainInfo, Backend as ChainBackend, - HeaderBackend as ChainHeaderBackend, ProvideCache, Cache, -}; -use crate::call_executor::{CallExecutor, LocalCallExecutor}; use executor::{RuntimeVersion, RuntimeInfo}; -use crate::notifications::{StorageNotifications, StorageEventStream}; -use crate::light::{call_executor::prove_execution, fetcher::ChangesProof}; -use crate::cht; -use crate::error; -use crate::in_mem; -use crate::block_builder::{self, api::BlockBuilder as BlockBuilderAPI}; -use crate::genesis; -use substrate_telemetry::{telemetry, SUBSTRATE_INFO}; - -use log::{info, trace, warn}; +use consensus::{ + Error as ConsensusError, BlockImportParams, + ImportResult, BlockOrigin, ForkChoiceStrategy, + well_known_cache_keys::Id as CacheKeyId, + SelectChain, self, +}; +use crate::{ + runtime_api::{ + CallRuntimeAt, ConstructRuntimeApi, Core as CoreApi, ProofRecorder, + InitializeBlock, + }, + backend::{ + self, BlockImportOperation, PrunableStateChangesTrieStorage, + StorageCollection, ChildStorageCollection + }, + blockchain::{ + self, Info as ChainInfo, Backend as ChainBackend, + HeaderBackend as ChainHeaderBackend, ProvideCache, Cache, + }, + call_executor::{CallExecutor, LocalCallExecutor}, + notifications::{StorageNotifications, StorageEventStream}, + light::{call_executor::prove_execution, fetcher::ChangesProof}, + block_builder::{self, api::BlockBuilder as BlockBuilderAPI}, + error::Error, + cht, error, in_mem, genesis +}; /// Type that implements `futures::Stream` of block import events. pub type ImportNotifications = mpsc::UnboundedReceiver>; @@ -260,6 +253,7 @@ impl PrePostHeader { pub fn new_in_mem( executor: E, genesis_storage: S, + keystore: Option, ) -> error::Result, LocalCallExecutor, E>, @@ -270,7 +264,7 @@ pub fn new_in_mem( S: BuildStorage, Block: BlockT, { - new_with_backend(Arc::new(in_mem::Backend::new()), executor, genesis_storage) + new_with_backend(Arc::new(in_mem::Backend::new()), executor, genesis_storage, keystore) } /// Create a client with the explicitly provided backend. @@ -279,6 +273,7 @@ pub fn new_with_backend( backend: Arc, executor: E, build_genesis_storage: S, + keystore: Option, ) -> error::Result, Block, RA>> where E: CodeExecutor + RuntimeInfo, @@ -286,10 +281,24 @@ pub fn new_with_backend( Block: BlockT, B: backend::LocalBackend { - let call_executor = LocalCallExecutor::new(backend.clone(), executor); + let call_executor = LocalCallExecutor::new(backend.clone(), executor, keystore); Client::new(backend, call_executor, build_genesis_storage, Default::default()) } +/// Figure out the block type for a given type (for now, just a `Client`). +pub trait BlockOf { + /// The type of the block. + type Type: BlockT; +} + +impl BlockOf for Client where + B: backend::Backend, + E: CallExecutor, + Block: BlockT, +{ + type Type = Block; +} + impl Client where B: backend::Backend, E: CallExecutor, @@ -362,7 +371,8 @@ impl Client where pub fn storage(&self, id: &BlockId, key: &StorageKey) -> error::Result> { Ok(self.state_at(id)? .storage(&key.0).map_err(|e| error::Error::from_state(Box::new(e)))? - .map(StorageData)) + .map(StorageData) + ) } /// Given a `BlockId` and a key, return the value under the hash in that block. @@ -1414,6 +1424,8 @@ impl CallRuntimeAt for Client where context: ExecutionContext, recorder: &Option>>>, ) -> error::Result> { + let enable_keystore = context.enable_keystore(); + let manager = match context { ExecutionContext::BlockConstruction => self.execution_strategies.block_construction.get_manager(), @@ -1443,6 +1455,7 @@ impl CallRuntimeAt for Client where native_call, offchain_extensions.as_mut(), recorder, + enable_keystore, ) } diff --git a/core/client/src/genesis.rs b/core/client/src/genesis.rs index bc401c8cd99a2..a7700b7d9f430 100644 --- a/core/client/src/genesis.rs +++ b/core/client/src/genesis.rs @@ -96,6 +96,7 @@ mod tests { &executor(), "Core_initialize_block", &header.encode(), + None, ).execute( ExecutionStrategy::NativeElseWasm, ).unwrap(); @@ -109,6 +110,7 @@ mod tests { &executor(), "BlockBuilder_apply_extrinsic", &tx.encode(), + None, ).execute( ExecutionStrategy::NativeElseWasm, ).unwrap(); @@ -122,6 +124,7 @@ mod tests { &executor(), "BlockBuilder_finalize_block", &[], + None, ).execute( ExecutionStrategy::NativeElseWasm, ).unwrap(); @@ -148,7 +151,7 @@ mod tests { #[test] fn construct_genesis_should_work_with_native() { let mut storage = GenesisConfig::new(false, - vec![Sr25519Keyring::One.into(), Sr25519Keyring::Two.into()], + vec![Sr25519Keyring::One.public().into(), Sr25519Keyring::Two.public().into()], vec![AccountKeyring::One.into(), AccountKeyring::Two.into()], 1000, None, @@ -170,6 +173,7 @@ mod tests { &executor(), "Core_execute_block", &b1data, + None, ).execute( ExecutionStrategy::NativeElseWasm, ).unwrap(); @@ -178,7 +182,7 @@ mod tests { #[test] fn construct_genesis_should_work_with_wasm() { let mut storage = GenesisConfig::new(false, - vec![Sr25519Keyring::One.into(), Sr25519Keyring::Two.into()], + vec![Sr25519Keyring::One.public().into(), Sr25519Keyring::Two.public().into()], vec![AccountKeyring::One.into(), AccountKeyring::Two.into()], 1000, None, @@ -200,6 +204,7 @@ mod tests { &executor(), "Core_execute_block", &b1data, + None, ).execute( ExecutionStrategy::AlwaysWasm, ).unwrap(); @@ -208,7 +213,7 @@ mod tests { #[test] fn construct_genesis_with_bad_transaction_should_panic() { let mut storage = GenesisConfig::new(false, - vec![Sr25519Keyring::One.into(), Sr25519Keyring::Two.into()], + vec![Sr25519Keyring::One.public().into(), Sr25519Keyring::Two.public().into()], vec![AccountKeyring::One.into(), AccountKeyring::Two.into()], 68, None, @@ -230,6 +235,7 @@ mod tests { &executor(), "Core_execute_block", &b1data, + None, ).execute( ExecutionStrategy::NativeElseWasm, ); diff --git a/core/client/src/lib.rs b/core/client/src/lib.rs index aa16e20cd8e1e..ba5f316d0c6d6 100644 --- a/core/client/src/lib.rs +++ b/core/client/src/lib.rs @@ -62,7 +62,8 @@ //! backend.clone(), //! LocalCallExecutor::new( //! backend.clone(), -//! NativeExecutor::::new(None) +//! NativeExecutor::::new(None), +//! None, //! ), //! // This parameter provides the storage for the chain genesis. //! <(StorageOverlay, ChildrenStorageOverlay)>::default(), @@ -114,7 +115,7 @@ pub use crate::client::{ new_in_mem, BlockBody, BlockStatus, ImportNotifications, FinalityNotifications, BlockchainEvents, BlockImportNotification, Client, ClientInfo, ExecutionStrategies, FinalityNotification, - LongestChain, + LongestChain, BlockOf, utils, }; #[cfg(feature = "std")] diff --git a/core/client/src/light/call_executor.rs b/core/client/src/light/call_executor.rs index 9d8df1c5417d3..2367aaf806510 100644 --- a/core/client/src/light/call_executor.rs +++ b/core/client/src/light/call_executor.rs @@ -94,8 +94,8 @@ where call_data: &[u8], _strategy: ExecutionStrategy, _side_effects_handler: Option<&mut O>, - ) - -> ClientResult> { + ) -> ClientResult> + { let block_hash = self.blockchain.expect_block_hash_from_id(id)?; let block_header = self.blockchain.expect_header(id.clone())?; @@ -130,6 +130,7 @@ where _native_call: Option, side_effects_handler: Option<&mut O>, _recorder: &Option>>>, + _enable_keystore: bool, ) -> ClientResult> where ExecutionManager: Clone { let block_initialized = match initialize_block { InitializeBlock::Do(ref init_block) => { @@ -143,11 +144,23 @@ where return Err(ClientError::NotAvailableOnLightClient.into()); } - self.call(at, method, call_data, (&execution_manager).into(), side_effects_handler).map(NativeOrEncoded::Encoded) + self.call( + at, + method, + call_data, + (&execution_manager).into(), + side_effects_handler, + ).map(NativeOrEncoded::Encoded) } fn runtime_version(&self, id: &BlockId) -> ClientResult { - let call_result = self.call(id, "Core_version", &[], ExecutionStrategy::NativeElseWasm, NeverOffchainExt::new())?; + let call_result = self.call( + id, + "Core_version", + &[], + ExecutionStrategy::NativeElseWasm, + NeverOffchainExt::new() + )?; RuntimeVersion::decode(&mut call_result.as_slice()) .map_err(|_| ClientError::VersionInvalid.into()) } @@ -270,6 +283,7 @@ impl CallExecutor for native_call: Option, side_effects_handler: Option<&mut O>, recorder: &Option>>>, + enable_keystore: bool, ) -> ClientResult> where ExecutionManager: Clone { // there's no actual way/need to specify native/wasm execution strategy on light node // => we can safely ignore passed values @@ -296,6 +310,7 @@ impl CallExecutor for native_call, side_effects_handler, recorder, + enable_keystore, ).map_err(|e| ClientError::Execution(Box::new(e.to_string()))), false => CallExecutor::contextual_call::< _, @@ -318,6 +333,7 @@ impl CallExecutor for native_call, side_effects_handler, recorder, + enable_keystore, ).map_err(|e| ClientError::Execution(Box::new(e.to_string()))), } } @@ -463,6 +479,7 @@ pub fn check_execution_proof( executor, "Core_initialize_block", &next_block.encode(), + None, )?; // execute method @@ -472,6 +489,7 @@ pub fn check_execution_proof( executor, &request.method, &request.call_data, + None, )?; Ok(local_result) diff --git a/core/client/src/light/fetcher.rs b/core/client/src/light/fetcher.rs index e80541a096883..22d3e8fdb7d4e 100644 --- a/core/client/src/light/fetcher.rs +++ b/core/client/src/light/fetcher.rs @@ -424,7 +424,6 @@ impl FetchChecker for LightDataChecker, body: Vec ) -> ClientResult> { - // TODO: #2621 let extrinsics_root = HashFor::::ordered_trie_root(body.iter().map(Encode::encode)); if *request.header.extrinsics_root() == extrinsics_root { diff --git a/core/client/src/light/mod.rs b/core/client/src/light/mod.rs index 69ba4d9f73ce3..89d3c60ddc372 100644 --- a/core/client/src/light/mod.rs +++ b/core/client/src/light/mod.rs @@ -73,7 +73,7 @@ pub fn new_light( E: CodeExecutor + RuntimeInfo, { let remote_executor = RemoteCallExecutor::new(backend.blockchain().clone(), fetcher); - let local_executor = LocalCallExecutor::new(backend.clone(), code_executor); + let local_executor = LocalCallExecutor::new(backend.clone(), code_executor, None); let executor = RemoteOrLocalCallExecutor::new(backend.clone(), remote_executor, local_executor); Client::new(backend, executor, genesis_storage, Default::default()) } diff --git a/core/consensus/aura/Cargo.toml b/core/consensus/aura/Cargo.toml index 98f233b8f4469..c27aa5df6dd6c 100644 --- a/core/consensus/aura/Cargo.toml +++ b/core/consensus/aura/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0" } primitives = { package = "substrate-primitives", path = "../../primitives" } +app-crypto = { package = "substrate-application-crypto", path = "../../application-crypto" } runtime_support = { package = "srml-support", path = "../../../srml/support" } runtime_version = { package = "sr-version", path = "../../sr-version" } runtime_io = { package = "sr-io", path = "../../sr-io" } @@ -17,12 +18,13 @@ inherents = { package = "substrate-inherents", path = "../../inherents" } srml-aura = { path = "../../../srml/aura" } client = { package = "substrate-client", path = "../../client" } substrate-telemetry = { path = "../../telemetry" } +substrate-keystore = { path = "../../keystore" } consensus_common = { package = "substrate-consensus-common", path = "../common" } sr-primitives = { path = "../../sr-primitives" } futures-preview = { version = "0.3.0-alpha.17", features = ["compat"] } futures01 = { package = "futures", version = "0.1" } futures-timer = "0.2.1" -parking_lot = "0.8.0" +parking_lot = "0.9.0" log = "0.4" [dev-dependencies] diff --git a/core/consensus/aura/primitives/Cargo.toml b/core/consensus/aura/primitives/Cargo.toml index ea8ffb420007c..ac2c2c791b2b1 100644 --- a/core/consensus/aura/primitives/Cargo.toml +++ b/core/consensus/aura/primitives/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false } substrate-client = { path = "../../../client", default-features = false } -primitives = { package = "substrate-primitives", path = "../../../primitives", default-features = false } +app-crypto = { package = "substrate-application-crypto", path = "../../../application-crypto", default-features = false } rstd = { package = "sr-std", path = "../../../sr-std", default-features = false } sr-primitives = { path = "../../../sr-primitives", default-features = false } @@ -19,5 +19,5 @@ std = [ "codec/std", "sr-primitives/std", "substrate-client/std", - "primitives/std", + "app-crypto/std", ] diff --git a/core/consensus/aura/primitives/src/lib.rs b/core/consensus/aura/primitives/src/lib.rs index 1a0aa5583ff98..070eb6c6a91a4 100644 --- a/core/consensus/aura/primitives/src/lib.rs +++ b/core/consensus/aura/primitives/src/lib.rs @@ -23,6 +23,40 @@ use substrate_client::decl_runtime_apis; use rstd::vec::Vec; use sr_primitives::ConsensusEngineId; +mod app_sr25519 { + use app_crypto::{app_crypto, key_types::AURA, sr25519}; + app_crypto!(sr25519, AURA); +} + +pub mod sr25519 { + /// An Aura authority keypair using S/R 25519 as its crypto. + #[cfg(feature = "std")] + pub type AuthorityPair = super::app_sr25519::Pair; + + /// An Aura authority signature using S/R 25519 as its crypto. + pub type AuthoritySignature = super::app_sr25519::Signature; + + /// An Aura authority identifier using S/R 25519 as its crypto. + pub type AuthorityId = super::app_sr25519::Public; +} + +mod app_ed25519 { + use app_crypto::{app_crypto, key_types::AURA, ed25519}; + app_crypto!(ed25519, AURA); +} + +pub mod ed25519 { + /// An Aura authority keypair using Ed25519 as its crypto. + #[cfg(feature = "std")] + pub type AuthorityPair = super::app_ed25519::Pair; + + /// An Aura authority signature using Ed25519 as its crypto. + pub type AuthoritySignature = super::app_ed25519::Signature; + + /// An Aura authority identifier using Ed25519 as its crypto. + pub type AuthorityId = super::app_ed25519::Public; +} + /// The `ConsensusEngineId` of AuRa. pub const AURA_ENGINE_ID: ConsensusEngineId = [b'a', b'u', b'r', b'a']; diff --git a/core/consensus/aura/src/lib.rs b/core/consensus/aura/src/lib.rs index 15966c611d044..ffce6dad32568 100644 --- a/core/consensus/aura/src/lib.rs +++ b/core/consensus/aura/src/lib.rs @@ -44,12 +44,13 @@ use client::{ runtime_api::ApiExt, error::Result as CResult, backend::AuxStore, + BlockOf }; use sr_primitives::{generic::{self, BlockId, OpaqueDigestItemId}, Justification}; use sr_primitives::traits::{Block as BlockT, Header, DigestItemFor, ProvideRuntimeApi, Zero, Member}; -use primitives::Pair; +use primitives::crypto::Pair; use inherents::{InherentDataProviders, InherentData}; use futures::{prelude::*, future}; @@ -141,7 +142,7 @@ pub fn start_aura( force_authoring: bool, ) -> Result, consensus_common::Error> where B: BlockT, - C: ProvideRuntimeApi + ProvideCache + AuxStore + Send + Sync, + C: ProvideRuntimeApi + BlockOf + ProvideCache + AuxStore + Send + Sync, C::Api: AuraApi>, SC: SelectChain, E::Proposer: Proposer, @@ -188,7 +189,7 @@ struct AuraWorker { impl SlotWorker for AuraWorker where B: BlockT, - C: ProvideRuntimeApi + ProvideCache + Sync, + C: ProvideRuntimeApi + BlockOf + ProvideCache + Sync, C::Api: AuraApi>, E: Environment, E::Proposer: Proposer, @@ -397,7 +398,7 @@ fn check_header( DigestItemFor: CompatibleDigestItem

, P::Signature: Decode, C: client::backend::AuxStore, - P::Public: AsRef + Encode + Decode + PartialEq + Clone, + P::Public: Encode + Decode + PartialEq + Clone, { let seal = match header.digest_mut().pop() { Some(x) => x, @@ -507,11 +508,11 @@ impl AuraVerifier #[forbid(deprecated)] impl Verifier for AuraVerifier where - C: ProvideRuntimeApi + Send + Sync + client::backend::AuxStore + ProvideCache, + C: ProvideRuntimeApi + Send + Sync + client::backend::AuxStore + ProvideCache + BlockOf, C::Api: BlockBuilderApi + AuraApi>, DigestItemFor: CompatibleDigestItem

, P: Pair + Send + Sync + 'static, - P::Public: Send + Sync + Hash + Eq + Clone + Decode + Encode + Debug + AsRef + 'static, + P::Public: Send + Sync + Hash + Eq + Clone + Decode + Encode + Debug + 'static, P::Signature: Encode + Decode, { fn verify( @@ -610,7 +611,7 @@ impl Verifier for AuraVerifier where fn initialize_authorities_cache(client: &C) -> Result<(), ConsensusError> where A: Codec, B: BlockT, - C: ProvideRuntimeApi + ProvideCache, + C: ProvideRuntimeApi + BlockOf + ProvideCache, C::Api: AuraApi, { // no cache => no initialization @@ -644,7 +645,7 @@ fn initialize_authorities_cache(client: &C) -> Result<(), ConsensusErro fn authorities(client: &C, at: &BlockId) -> Result, ConsensusError> where A: Codec, B: BlockT, - C: ProvideRuntimeApi + ProvideCache, + C: ProvideRuntimeApi + BlockOf + ProvideCache, C::Api: AuraApi, { client @@ -685,11 +686,11 @@ pub fn import_queue( inherent_data_providers: InherentDataProviders, ) -> Result, consensus_common::Error> where B: BlockT, - C: 'static + ProvideRuntimeApi + ProvideCache + Send + Sync + AuxStore, + C: 'static + ProvideRuntimeApi + BlockOf + ProvideCache + Send + Sync + AuxStore, C::Api: BlockBuilderApi + AuraApi>, DigestItemFor: CompatibleDigestItem

, P: Pair + Send + Sync + 'static, - P::Public: Clone + Eq + Send + Sync + Hash + Debug + Encode + Decode + AsRef, + P::Public: Clone + Eq + Send + Sync + Hash + Debug + Encode + Decode, P::Signature: Encode + Decode, { register_aura_inherent_data_provider(&inherent_data_providers, slot_duration.get())?; @@ -721,9 +722,9 @@ mod tests { use parking_lot::Mutex; use tokio::runtime::current_thread; use keyring::sr25519::Keyring; - use primitives::sr25519; use client::{LongestChain, BlockchainEvents}; use test_client; + use aura_primitives::sr25519::AuthorityPair; type Error = client::error::Error; @@ -771,7 +772,7 @@ mod tests { impl TestNetFactory for AuraTestNet { type Specialization = DummySpecialization; - type Verifier = AuraVerifier; + type Verifier = AuraVerifier; type PeerData = (); /// Create new test network with peers and given config. @@ -855,9 +856,9 @@ mod tests { &inherent_data_providers, slot_duration.get() ).expect("Registers aura inherent data provider"); - let aura = start_aura::<_, _, _, _, _, sr25519::Pair, _, _, _>( + let aura = start_aura::<_, _, _, _, _, AuthorityPair, _, _, _>( slot_duration, - Arc::new(key.clone().into()), + Arc::new(key.clone().pair().into()), client.clone(), select_chain, client, @@ -885,9 +886,9 @@ mod tests { assert_eq!(client.info().chain.best_number, 0); assert_eq!(authorities(&client, &BlockId::Number(0)).unwrap(), vec![ - Keyring::Alice.into(), - Keyring::Bob.into(), - Keyring::Charlie.into() + Keyring::Alice.public().into(), + Keyring::Bob.public().into(), + Keyring::Charlie.public().into() ]); } } diff --git a/core/consensus/babe/Cargo.toml b/core/consensus/babe/Cargo.toml index 8bcade80232cc..9e1fb6bd4b089 100644 --- a/core/consensus/babe/Cargo.toml +++ b/core/consensus/babe/Cargo.toml @@ -9,24 +9,26 @@ edition = "2018" codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } babe_primitives = { package = "substrate-consensus-babe-primitives", path = "primitives" } primitives = { package = "substrate-primitives", path = "../../primitives" } +app-crypto = { package = "substrate-application-crypto", path = "../../application-crypto" } num-bigint = "0.2" num-rational = "0.2" num-traits = "0.2" -runtime_support = { package = "srml-support", path = "../../../srml/support" } -runtime_version = { package = "sr-version", path = "../../sr-version" } -runtime_io = { package = "sr-io", path = "../../sr-io" } +runtime-support = { package = "srml-support", path = "../../../srml/support" } +runtime-version = { package = "sr-version", path = "../../sr-version" } +runtime-io = { package = "sr-io", path = "../../sr-io" } inherents = { package = "substrate-inherents", path = "../../inherents" } substrate-telemetry = { path = "../../telemetry" } +keystore = { package = "substrate-keystore", path = "../../keystore" } srml-babe = { path = "../../../srml/babe" } client = { package = "substrate-client", path = "../../client" } -consensus_common = { package = "substrate-consensus-common", path = "../common" } +consensus-common = { package = "substrate-consensus-common", path = "../common" } slots = { package = "substrate-consensus-slots", path = "../slots" } sr-primitives = { path = "../../sr-primitives" } fork-tree = { path = "../../utils/fork-tree" } futures-preview = { version = "0.3.0-alpha.17", features = ["compat"] } futures01 = { package = "futures", version = "0.1" } futures-timer = "0.2.1" -parking_lot = "0.8.0" +parking_lot = "0.9.0" log = "0.4.6" schnorrkel = { version = "0.8.4", features = ["preaudit_deprecated"] } rand = "0.6.5" @@ -40,6 +42,7 @@ service = { package = "substrate-service", path = "../../service" } test-client = { package = "substrate-test-runtime-client", path = "../../test-runtime/client" } tokio = "0.1.18" env_logger = "0.6.1" +tempfile = "3.1" [features] test-helpers = [] diff --git a/core/consensus/babe/primitives/Cargo.toml b/core/consensus/babe/primitives/Cargo.toml index 3948c9727caf3..5f7fbe4fd8129 100644 --- a/core/consensus/babe/primitives/Cargo.toml +++ b/core/consensus/babe/primitives/Cargo.toml @@ -9,7 +9,7 @@ edition = "2018" substrate-client = { path = "../../../client", default-features = false } rstd = { package = "sr-std", path = "../../../sr-std", default-features = false } sr-primitives = { path = "../../../sr-primitives", default-features = false } -primitives = { package = "substrate-primitives", path = "../../../primitives", default-features = false } +app-crypto = { package = "substrate-application-crypto", path = "../../../application-crypto", default-features = false } slots = { package = "substrate-consensus-slots", path = "../../slots", optional = true } schnorrkel = { version = "0.8.4", features = ["preaudit_deprecated"], optional = true } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false } @@ -23,4 +23,5 @@ std = [ "codec/std", "schnorrkel", "slots", + "app-crypto/std", ] diff --git a/core/consensus/babe/primitives/src/digest.rs b/core/consensus/babe/primitives/src/digest.rs index 81effef5d6e60..3b6e3221bd8a8 100644 --- a/core/consensus/babe/primitives/src/digest.rs +++ b/core/consensus/babe/primitives/src/digest.rs @@ -17,7 +17,7 @@ //! Private implementation details of BABE digests. #[cfg(feature = "std")] -use primitives::sr25519::Signature; +use super::AuthoritySignature; #[cfg(feature = "std")] use super::{BABE_ENGINE_ID, Epoch}; #[cfg(not(feature = "std"))] @@ -111,10 +111,10 @@ pub trait CompatibleDigestItem: Sized { fn as_babe_pre_digest(&self) -> Option; /// Construct a digest item which contains a BABE seal. - fn babe_seal(signature: Signature) -> Self; + fn babe_seal(signature: AuthoritySignature) -> Self; /// If this item is a BABE signature, return the signature. - fn as_babe_seal(&self) -> Option; + fn as_babe_seal(&self) -> Option; /// If this item is a BABE epoch, return it. fn as_babe_epoch(&self) -> Option; @@ -132,11 +132,11 @@ impl CompatibleDigestItem for DigestItem where self.try_to(OpaqueDigestItemId::PreRuntime(&BABE_ENGINE_ID)) } - fn babe_seal(signature: Signature) -> Self { + fn babe_seal(signature: AuthoritySignature) -> Self { DigestItem::Seal(BABE_ENGINE_ID, signature.encode()) } - fn as_babe_seal(&self) -> Option { + fn as_babe_seal(&self) -> Option { self.try_to(OpaqueDigestItemId::Seal(&BABE_ENGINE_ID)) } diff --git a/core/consensus/babe/primitives/src/lib.rs b/core/consensus/babe/primitives/src/lib.rs index f076c3ae2a10d..f4da908080c8b 100644 --- a/core/consensus/babe/primitives/src/lib.rs +++ b/core/consensus/babe/primitives/src/lib.rs @@ -24,21 +24,28 @@ mod digest; use codec::{Encode, Decode}; use rstd::vec::Vec; use sr_primitives::ConsensusEngineId; -use primitives::sr25519; use substrate_client::decl_runtime_apis; #[cfg(feature = "std")] pub use digest::{BabePreDigest, CompatibleDigestItem}; pub use digest::{BABE_VRF_PREFIX, RawBabePreDigest}; +mod app { + use app_crypto::{app_crypto, key_types::BABE, sr25519}; + app_crypto!(sr25519, BABE); +} + /// A Babe authority keypair. Necessarily equivalent to the schnorrkel public key used in /// the main Babe module. If that ever changes, then this must, too. #[cfg(feature = "std")] -pub type AuthorityPair = sr25519::Pair; +pub type AuthorityPair = app::Pair; + +/// A Babe authority signature. +pub type AuthoritySignature = app::Signature; /// A Babe authority identifier. Necessarily equivalent to the schnorrkel public key used in /// the main Babe module. If that ever changes, then this must, too. -pub type AuthorityId = sr25519::Public; +pub type AuthorityId = app::Public; /// The `ConsensusEngineId` of BABE. pub const BABE_ENGINE_ID: ConsensusEngineId = *b"BABE"; diff --git a/core/consensus/babe/src/lib.rs b/core/consensus/babe/src/lib.rs index 722c63bb9530e..c8b9f255595c5 100644 --- a/core/consensus/babe/src/lib.rs +++ b/core/consensus/babe/src/lib.rs @@ -18,9 +18,11 @@ //! //! BABE (Blind Assignment for Blockchain Extension) consensus in Substrate. -#![forbid(unsafe_code, missing_docs, unused_must_use, unused_imports, unused_variables)] +#![forbid(unsafe_code, missing_docs)] pub use babe_primitives::*; pub use consensus_common::SyncOracle; +use std::{collections::HashMap, sync::Arc, u64, fmt::{Debug, Display}, pin::Pin, time::{Instant, Duration}}; +use babe_primitives; use consensus_common::ImportResult; use consensus_common::import_queue::{ BoxJustificationImport, BoxFinalityProofImport, @@ -31,11 +33,11 @@ use sr_primitives::traits::{ Block as BlockT, Header, DigestItemFor, NumberFor, ProvideRuntimeApi, SimpleBitOps, Zero, }; -use std::{collections::HashMap, sync::Arc, u64, fmt::{Debug, Display}, pin::Pin, time::{Instant, Duration}}; +use keystore::KeyStorePtr; use runtime_support::serde::{Serialize, Deserialize}; use codec::{Decode, Encode}; use parking_lot::{Mutex, MutexGuard}; -use primitives::{Blake2Hasher, H256, Pair, Public, sr25519}; +use primitives::{Blake2Hasher, H256, Pair, Public}; use merlin::Transcript; use inherents::{InherentDataProviders, InherentData}; use substrate_telemetry::{ @@ -63,12 +65,8 @@ use consensus_common::{SelectChain, well_known_cache_keys}; use consensus_common::import_queue::{Verifier, BasicQueue}; use client::{ block_builder::api::BlockBuilder as BlockBuilderApi, - blockchain::{self, HeaderBackend, ProvideCache}, - BlockchainEvents, - CallExecutor, Client, - runtime_api::ApiExt, - error::Result as ClientResult, - backend::{AuxStore, Backend}, + blockchain::{self, HeaderBackend, ProvideCache}, BlockchainEvents, CallExecutor, Client, + runtime_api::ApiExt, error::Result as ClientResult, backend::{AuxStore, Backend}, utils::is_descendent_of, }; use fork_tree::ForkTree; @@ -135,13 +133,12 @@ impl SlotCompatible for BabeLink { /// Parameters for BABE. pub struct BabeParams { - /// The configuration for BABE. Includes the slot duration, threshold, and /// other parameters. pub config: Config, - /// The key of the node we are running on. - pub local_key: Arc, + /// The keystore that manages the keys of the node. + pub keystore: KeyStorePtr, /// The client to use pub client: Arc, @@ -171,8 +168,8 @@ pub struct BabeParams { /// Start the babe worker. The returned future should be run in a tokio runtime. pub fn start_babe(BabeParams { config, - local_key, client, + keystore, select_chain, block_import, env, @@ -200,10 +197,10 @@ pub fn start_babe(BabeParams { client: client.clone(), block_import: Arc::new(Mutex::new(block_import)), env, - local_key, sync_oracle: sync_oracle.clone(), force_authoring, c: config.c(), + keystore, }; register_babe_inherent_data_provider(&inherent_data_providers, config.0.slot_duration())?; Ok(slots::start_slot_worker( @@ -220,10 +217,10 @@ struct BabeWorker { client: Arc, block_import: Arc>, env: E, - local_key: Arc, sync_oracle: SO, force_authoring: bool, c: (u64, u64), + keystore: KeyStorePtr, } impl SlotWorker for BabeWorker where @@ -248,7 +245,6 @@ impl SlotWorker for BabeWorker w chain_head: B::Header, slot_info: SlotInfo, ) -> Self::OnSlot { - let pair = self.local_key.clone(); let ref client = self.client; let block_import = self.block_import.clone(); @@ -288,10 +284,10 @@ impl SlotWorker for BabeWorker w let proposal_work = if let Some(claim) = claim_slot( slot_info.number, epoch, - &pair, self.c, + &self.keystore, ) { - let ((inout, vrf_proof, _batchable_proof), authority_index) = claim; + let ((inout, vrf_proof, _batchable_proof), authority_index, key) = claim; debug!( target: "babe", "Starting authorship at slot {}; timestamp = {}", @@ -340,7 +336,7 @@ impl SlotWorker for BabeWorker w Delay::new(remaining_duration) .map_err(|err| consensus_common::Error::FaultyTimer(err).into()) ).map(|v| match v { - futures::future::Either::Left((v, _)) => v, + futures::future::Either::Left((v, _)) => v.map(|v| (v, key)), futures::future::Either::Right((Ok(_), _)) => Err(consensus_common::Error::ClientImport("Timeout in the BaBe proposer".into())), futures::future::Either::Right((Err(err), _)) => Err(err), @@ -349,7 +345,7 @@ impl SlotWorker for BabeWorker w return Box::pin(future::ready(Ok(()))); }; - Box::pin(proposal_work.map_ok(move |b| { + Box::pin(proposal_work.map_ok(move |(b, key)| { // minor hack since we don't have access to the timestamp // that is actually set by the proposer. let slot_after_building = SignedDuration::default().slot_now(slot_duration); @@ -372,7 +368,7 @@ impl SlotWorker for BabeWorker w // sign the pre-sealed hash of the block and then // add it to a digest item. let header_hash = header.hash(); - let signature = pair.sign(header_hash.as_ref()); + let signature = key.sign(header_hash.as_ref()); let signature_digest_item = DigestItemFor::::babe_seal(signature); let import_block = BlockImportParams:: { @@ -496,7 +492,7 @@ fn check_header( } else { let (pre_hash, author) = (header.hash(), &authorities[authority_index as usize].0); - if sr25519::Pair::verify(&sig, pre_hash, author.clone()) { + if AuthorityPair::verify(&sig, pre_hash, &author) { let (inout, _batchable_proof) = { let transcript = make_transcript( &randomness, @@ -769,8 +765,9 @@ fn register_babe_inherent_data_provider( } } -fn get_keypair(q: &sr25519::Pair) -> &Keypair { - q.as_ref() +fn get_keypair(q: &AuthorityPair) -> &Keypair { + use primitives::crypto::IsWrappedBy; + primitives::sr25519::Pair::from_ref(q).as_ref() } #[allow(deprecated)] @@ -823,11 +820,15 @@ fn calculate_threshold( fn claim_slot( slot_number: u64, Epoch { ref authorities, ref randomness, epoch_index, .. }: Epoch, - key: &sr25519::Pair, c: (u64, u64), -) -> Option<((VRFInOut, VRFProof, VRFProofBatchable), usize)> { - let public = &key.public(); - let authority_index = authorities.iter().position(|s| &s.0 == public)?; + keystore: &KeyStorePtr, +) -> Option<((VRFInOut, VRFProof, VRFProofBatchable), usize, AuthorityPair)> { + let keystore = keystore.read(); + let (key_pair, authority_index) = authorities.iter() + .enumerate() + .find_map(|(i, a)| { + keystore.key_pair::(&a.0).ok().map(|kp| (kp, i)) + })?; let transcript = make_transcript(randomness, slot_number, epoch_index); // Compute the threshold we will use. @@ -836,9 +837,9 @@ fn claim_slot( // be empty. Therefore, this division in `calculate_threshold` is safe. let threshold = calculate_threshold(c, authorities, authority_index); - get_keypair(key) + get_keypair(&key_pair) .vrf_sign_after_check(transcript, |inout| check(inout, threshold)) - .map(|s|(s, authority_index)) + .map(|s|(s, authority_index, key_pair)) } fn initialize_authorities_cache(client: &C) -> Result<(), ConsensusError> where @@ -1201,8 +1202,8 @@ pub mod test_helpers { client: &C, at: &BlockId, slot_number: u64, - key: &sr25519::Pair, c: (u64, u64), + keystore: &KeyStorePtr, ) -> Option where B: BlockT, C: ProvideRuntimeApi + ProvideCache, @@ -1213,9 +1214,9 @@ pub mod test_helpers { super::claim_slot( slot_number, epoch, - key, c, - ).map(|((inout, vrf_proof, _), authority_index)| { + keystore, + ).map(|((inout, vrf_proof, _), authority_index, _)| { BabePreDigest { vrf_proof, vrf_output: inout.to_output(), diff --git a/core/consensus/babe/src/tests.rs b/core/consensus/babe/src/tests.rs index a0e59e3e80ae1..f1983a99628d7 100644 --- a/core/consensus/babe/src/tests.rs +++ b/core/consensus/babe/src/tests.rs @@ -20,7 +20,9 @@ // https://github.com/paritytech/substrate/issues/2532 #![allow(deprecated)] use super::*; +use super::generic::DigestItem; +use babe_primitives::AuthorityPair; use client::{LongestChain, block_builder::BlockBuilder}; use consensus_common::NoNetwork as DummyOracle; use network::test::*; @@ -29,7 +31,6 @@ use sr_primitives::traits::{Block as BlockT, DigestFor}; use network::config::ProtocolConfig; use tokio::runtime::current_thread; use keyring::sr25519::Keyring; -use super::generic::DigestItem; use client::BlockchainEvents; use test_client; use log::debug; @@ -183,15 +184,21 @@ fn run_one_test() { let net = BabeTestNet::new(3); let peers = &[ - (0, Keyring::Alice), - (1, Keyring::Bob), - (2, Keyring::Charlie), + (0, "//Alice"), + (1, "//Bob"), + (2, "//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 keystore_paths = Vec::new(); + for (peer_id, seed) in peers { + let keystore_path = tempfile::tempdir().expect("Creates keystore path"); + let keystore = keystore::Store::open(keystore_path.path(), None).expect("Creates keystore"); + keystore.write().insert_ephemeral_from_seed::(seed).expect("Generates authority key"); + keystore_paths.push(keystore_path); + let client = net.lock().peer(*peer_id).client().as_full().unwrap(); let environ = DummyFactory(client.clone()); import_notifications.push( @@ -200,21 +207,18 @@ fn run_one_test() { .for_each(move |_| future::ready(())) ); - let config = Config::get_or_compute(&*client) - .expect("slot duration available"); + let config = Config::get_or_compute(&*client).expect("slot duration available"); let inherent_data_providers = InherentDataProviders::new(); register_babe_inherent_data_provider( &inherent_data_providers, config.get() ).expect("Registers babe inherent data provider"); - #[allow(deprecated)] let select_chain = LongestChain::new(client.backend().clone()); runtime.spawn(start_babe(BabeParams { config, - local_key: Arc::new(key.clone().into()), block_import: client.clone(), select_chain, client, @@ -223,6 +227,7 @@ fn run_one_test() { inherent_data_providers, force_authoring: false, time_source: Default::default(), + keystore, }).expect("Starts babe")); } @@ -230,7 +235,7 @@ fn run_one_test() { net.lock().poll(); Ok::<_, ()>(futures01::Async::NotReady::<()>) })); - + runtime.block_on(future::join_all(import_notifications) .map(|_| Ok::<(), ()>(())).compat()).unwrap(); } @@ -280,8 +285,8 @@ fn rejects_missing_consensus_digests() { #[test] fn wrong_consensus_engine_id_rejected() { let _ = env_logger::try_init(); - let sig = sr25519::Pair::generate().0.sign(b""); - let bad_seal: Item = DigestItem::Seal([0; 4], sig.0.to_vec()); + let sig = AuthorityPair::generate().0.sign(b""); + let bad_seal: Item = DigestItem::Seal([0; 4], sig.to_vec()); assert!(bad_seal.as_babe_pre_digest().is_none()); assert!(bad_seal.as_babe_seal().is_none()) } @@ -296,8 +301,8 @@ fn malformed_pre_digest_rejected() { #[test] fn sig_is_not_pre_digest() { let _ = env_logger::try_init(); - let sig = sr25519::Pair::generate().0.sign(b""); - let bad_seal: Item = DigestItem::Seal(BABE_ENGINE_ID, sig.0.to_vec()); + let sig = AuthorityPair::generate().0.sign(b""); + let bad_seal: Item = DigestItem::Seal(BABE_ENGINE_ID, sig.to_vec()); assert!(bad_seal.as_babe_pre_digest().is_none()); assert!(bad_seal.as_babe_seal().is_some()) } @@ -305,7 +310,11 @@ fn sig_is_not_pre_digest() { #[test] fn can_author_block() { let _ = env_logger::try_init(); - let (pair, _) = sr25519::Pair::generate(); + let keystore_path = tempfile::tempdir().expect("Creates keystore path"); + let keystore = keystore::Store::open(keystore_path.path(), None).expect("Creates keystore"); + let pair = keystore.write().insert_ephemeral_from_seed::("//Alice") + .expect("Generates authority pair"); + let mut i = 0; let epoch = Epoch { start_slot: 0, @@ -315,10 +324,10 @@ fn can_author_block() { duration: 100, }; loop { - match claim_slot(i, epoch.clone(), &pair, (3, 10)) { + match claim_slot(i, epoch.clone(), (3, 10), &keystore) { None => i += 1, Some(s) => { - debug!(target: "babe", "Authored block {:?}", s); + debug!(target: "babe", "Authored block {:?}", s.0); break } } @@ -332,8 +341,8 @@ fn authorities_call_works() { assert_eq!(client.info().chain.best_number, 0); assert_eq!(epoch(&client, &BlockId::Number(0)).unwrap().authorities, vec![ - (Keyring::Alice.into(), 1), - (Keyring::Bob.into(), 1), - (Keyring::Charlie.into(), 1), + (Keyring::Alice.public().into(), 1), + (Keyring::Bob.public().into(), 1), + (Keyring::Charlie.public().into(), 1), ]); } diff --git a/core/consensus/common/Cargo.toml b/core/consensus/common/Cargo.toml index b327e9456ae13..29f30e363d1b9 100644 --- a/core/consensus/common/Cargo.toml +++ b/core/consensus/common/Cargo.toml @@ -17,7 +17,7 @@ rstd = { package = "sr-std", path = "../../sr-std" } runtime_version = { package = "sr-version", path = "../../sr-version" } sr-primitives = { path = "../../sr-primitives" } codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } -parking_lot = "0.8.0" +parking_lot = "0.9.0" [dev-dependencies] test-client = { package = "substrate-test-runtime-client", path = "../../test-runtime/client" } diff --git a/core/consensus/rhd/Cargo.toml b/core/consensus/rhd/Cargo.toml index 3f6fef71a3739..801605c3f64ca 100644 --- a/core/consensus/rhd/Cargo.toml +++ b/core/consensus/rhd/Cargo.toml @@ -19,7 +19,7 @@ sr-primitives = { path = "../../sr-primitives" } runtime_version = { package = "sr-version", path = "../../sr-version" } runtime_io = { package = "sr-io", path = "../../sr-io" } tokio = "0.1.7" -parking_lot = "0.8.0" +parking_lot = "0.9.0" log = "0.4" rhododendron = { version = "0.7.0", features = ["codec"] } exit-future = "0.1" diff --git a/core/consensus/rhd/src/misbehaviour_check.rs b/core/consensus/rhd/src/misbehaviour_check.rs index e40311c14f323..bde3976c19b09 100644 --- a/core/consensus/rhd/src/misbehaviour_check.rs +++ b/core/consensus/rhd/src/misbehaviour_check.rs @@ -26,7 +26,7 @@ use runtime_io; fn check_message_sig( message: Message, signature: &Signature, - from: &AuthorityId + from: &AuthorityId, ) -> bool { let msg: Vec = message.encode(); runtime_io::ed25519_verify(&signature.0, &msg, from) diff --git a/core/consensus/slots/Cargo.toml b/core/consensus/slots/Cargo.toml index 30e3eb654c06b..857d1e3a6b94e 100644 --- a/core/consensus/slots/Cargo.toml +++ b/core/consensus/slots/Cargo.toml @@ -14,7 +14,7 @@ consensus_common = { package = "substrate-consensus-common", path = "../common" inherents = { package = "substrate-inherents", path = "../../inherents" } futures-preview = "0.3.0-alpha.17" futures-timer = "0.2.1" -parking_lot = "0.8.0" +parking_lot = "0.9.0" log = "0.4" [dev-dependencies] diff --git a/core/executor/Cargo.toml b/core/executor/Cargo.toml index c5b296b86675f..a4707fb89e3d4 100644 --- a/core/executor/Cargo.toml +++ b/core/executor/Cargo.toml @@ -18,7 +18,7 @@ wasmi = "0.5.0" parity-wasm = "0.31" byteorder = "1.3" lazy_static = "1.3" -parking_lot = "0.8.0" +parking_lot = "0.9.0" log = "0.4" libsecp256k1 = "0.2.1" tiny-keccak = "1.4.2" diff --git a/core/executor/runtime-test/src/lib.rs b/core/executor/runtime-test/src/lib.rs index 4c68d449f215b..49612c271e6d9 100644 --- a/core/executor/runtime-test/src/lib.rs +++ b/core/executor/runtime-test/src/lib.rs @@ -11,6 +11,7 @@ use runtime_io::{ set_storage, storage, clear_prefix, print, blake2_128, blake2_256, twox_128, twox_256, ed25519_verify, sr25519_verify, enumerated_trie_root }; +use primitives::{ed25519, sr25519}; macro_rules! impl_stubs { ( $( $new_name:ident => $invoke:expr, )* ) => { @@ -80,7 +81,7 @@ impl_stubs!( sig.copy_from_slice(&input[32..96]); let msg = b"all ok!"; - [ed25519_verify(&sig, &msg[..], &pubkey) as u8].to_vec() + [ed25519_verify(&ed25519::Signature(sig), &msg[..], &ed25519::Public(pubkey)) as u8].to_vec() }, test_sr25519_verify => |input: &[u8]| { let mut pubkey = [0; 32]; @@ -90,7 +91,7 @@ impl_stubs!( sig.copy_from_slice(&input[32..96]); let msg = b"all ok!"; - [sr25519_verify(&sig, &msg[..], &pubkey) as u8].to_vec() + [sr25519_verify(&sr25519::Signature(sig), &msg[..], &sr25519::Public(pubkey)) as u8].to_vec() }, test_enumerated_trie_root => |_| { enumerated_trie_root::( diff --git a/core/executor/src/wasm_executor.rs b/core/executor/src/wasm_executor.rs index 637390b2c7ff4..2c69c8ba0588b 100644 --- a/core/executor/src/wasm_executor.rs +++ b/core/executor/src/wasm_executor.rs @@ -27,11 +27,10 @@ use wasmi::{ use state_machine::{Externalities, ChildStorageKey}; use crate::error::{Error, Result}; use codec::Encode; -use primitives::{blake2_128, blake2_256, twox_64, twox_128, twox_256, ed25519, sr25519, Pair}; -use primitives::offchain; -use primitives::hexdisplay::HexDisplay; -use primitives::sandbox as sandbox_primitives; -use primitives::{H256, Blake2Hasher}; +use primitives::{ + blake2_128, blake2_256, twox_64, twox_128, twox_256, ed25519, sr25519, Pair, crypto::KeyTypeId, + offchain, hexdisplay::HexDisplay, sandbox as sandbox_primitives, H256, Blake2Hasher, +}; use trie::{TrieConfiguration, trie_types::Layout}; use crate::sandbox; use crate::allocator; @@ -641,7 +640,30 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, this.memory.set(out, &result).map_err(|_| "Invalid attempt to set result in ext_keccak_256")?; Ok(()) }, - ext_ed25519_verify(msg_data: *const u8, msg_len: u32, sig_data: *const u8, pubkey_data: *const u8) -> u32 => { + ext_ed25519_public_keys(id_data: *const u8, result_len: *mut u32) -> *mut u8 => { + let mut id = [0u8; 4]; + this.memory.get_into(id_data, &mut id[..]) + .map_err(|_| "Invalid attempt to get id in ext_ed25519_public_keys")?; + let key_type = KeyTypeId(id); + + let keys = runtime_io::ed25519_public_keys(key_type).encode(); + + let len = keys.len() as u32; + let offset = this.heap.allocate(len)? as u32; + + this.memory.set(offset, keys.as_ref()) + .map_err(|_| "Invalid attempt to set memory in ext_ed25519_public_keys")?; + this.memory.write_primitive(result_len, len) + .map_err(|_| "Invalid attempt to write result_len in ext_ed25519_public_keys")?; + + Ok(offset) + }, + ext_ed25519_verify( + msg_data: *const u8, + msg_len: u32, + sig_data: *const u8, + pubkey_data: *const u8, + ) -> u32 => { let mut sig = [0u8; 64]; this.memory.get_into(sig_data, &mut sig[..]) .map_err(|_| "Invalid attempt to get signature in ext_ed25519_verify")?; @@ -657,7 +679,87 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, 5 }) }, - ext_sr25519_verify(msg_data: *const u8, msg_len: u32, sig_data: *const u8, pubkey_data: *const u8) -> u32 => { + ext_ed25519_generate(id_data: *const u8, seed: *const u8, seed_len: u32, out: *mut u8) => { + let mut id = [0u8; 4]; + this.memory.get_into(id_data, &mut id[..]) + .map_err(|_| "Invalid attempt to get id in ext_ed25519_generate")?; + let key_type = KeyTypeId(id); + + let seed = if seed_len == 0 { + None + } else { + Some( + this.memory.get(seed, seed_len as usize) + .map_err(|_| "Invalid attempt to get seed in ext_ed25519_generate")? + ) + }; + + let seed = seed.as_ref() + .map(|seed| + std::str::from_utf8(&seed) + .map_err(|_| "Seed not a valid utf8 string in ext_sr25119_generate") + ).transpose()?; + + let pubkey = runtime_io::ed25519_generate(key_type, seed); + + this.memory.set(out, pubkey.as_ref()) + .map_err(|_| "Invalid attempt to set out in ext_ed25519_generate".into()) + }, + ext_ed25519_sign( + id_data: *const u8, + pubkey_data: *const u8, + msg_data: *const u8, + msg_len: u32, + out: *mut u8, + ) -> u32 => { + let mut id = [0u8; 4]; + this.memory.get_into(id_data, &mut id[..]) + .map_err(|_| "Invalid attempt to get id in ext_ed25519_sign")?; + let key_type = KeyTypeId(id); + + let mut pubkey = [0u8; 32]; + this.memory.get_into(pubkey_data, &mut pubkey[..]) + .map_err(|_| "Invalid attempt to get pubkey in ext_ed25519_sign")?; + + let msg = this.memory.get(msg_data, msg_len as usize) + .map_err(|_| "Invalid attempt to get message in ext_ed25519_sign")?; + + let signature = runtime_io::ed25519_sign(key_type, &ed25519::Public(pubkey), &msg); + + match signature { + Some(signature) => { + this.memory + .set(out, signature.as_ref()) + .map_err(|_| "Invalid attempt to set out in ext_ed25519_sign")?; + Ok(0) + }, + None => Ok(1), + } + }, + ext_sr25519_public_keys(id_data: *const u8, result_len: *mut u32) -> *mut u8 => { + let mut id = [0u8; 4]; + this.memory.get_into(id_data, &mut id[..]) + .map_err(|_| "Invalid attempt to get id in ext_sr25519_public_keys")?; + let key_type = KeyTypeId(id); + + let keys = runtime_io::sr25519_public_keys(key_type).encode(); + + let len = keys.len() as u32; + let offset = this.heap.allocate(len)? as u32; + + this.memory.set(offset, keys.as_ref()) + .map_err(|_| "Invalid attempt to set memory in ext_sr25519_public_keys")?; + this.memory.write_primitive(result_len, len) + .map_err(|_| "Invalid attempt to write result_len in ext_sr25519_public_keys")?; + + Ok(offset) + }, + ext_sr25519_verify( + msg_data: *const u8, + msg_len: u32, + sig_data: *const u8, + pubkey_data: *const u8, + ) -> u32 => { let mut sig = [0u8; 64]; this.memory.get_into(sig_data, &mut sig[..]) .map_err(|_| "Invalid attempt to get signature in ext_sr25519_verify")?; @@ -673,6 +775,62 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, 5 }) }, + ext_sr25519_generate(id_data: *const u8, seed: *const u8, seed_len: u32, out: *mut u8) => { + let mut id = [0u8; 4]; + this.memory.get_into(id_data, &mut id[..]) + .map_err(|_| "Invalid attempt to get id in ext_sr25519_generate")?; + let key_type = KeyTypeId(id); + let seed = if seed_len == 0 { + None + } else { + Some( + this.memory.get(seed, seed_len as usize) + .map_err(|_| "Invalid attempt to get seed in ext_sr25519_generate")? + ) + }; + + let seed = seed.as_ref() + .map(|seed| + std::str::from_utf8(&seed) + .map_err(|_| "Seed not a valid utf8 string in ext_sr25119_generate") + ) + .transpose()?; + + let pubkey = runtime_io::sr25519_generate(key_type, seed); + + this.memory.set(out, pubkey.as_ref()) + .map_err(|_| "Invalid attempt to set out in ext_sr25519_generate".into()) + }, + ext_sr25519_sign( + id_data: *const u8, + pubkey_data: *const u8, + msg_data: *const u8, + msg_len: u32, + out: *mut u8, + ) -> u32 => { + let mut id = [0u8; 4]; + this.memory.get_into(id_data, &mut id[..]) + .map_err(|_| "Invalid attempt to get id in ext_sr25519_sign")?; + let key_type = KeyTypeId(id); + + let mut pubkey = [0u8; 32]; + this.memory.get_into(pubkey_data, &mut pubkey[..]) + .map_err(|_| "Invalid attempt to get pubkey in ext_sr25519_sign")?; + + let msg = this.memory.get(msg_data, msg_len as usize) + .map_err(|_| "Invalid attempt to get message in ext_sr25519_sign")?; + + let signature = runtime_io::sr25519_sign(key_type, &sr25519::Public(pubkey), &msg); + + match signature { + Some(signature) => { + this.memory.set(out, signature.as_ref()) + .map_err(|_| "Invalid attempt to set out in ext_sr25519_sign")?; + Ok(0) + }, + None => Ok(1), + } + }, ext_secp256k1_ecdsa_recover(msg_data: *const u8, sig_data: *const u8, pubkey_data: *mut u8) -> u32 => { let mut sig = [0u8; 65]; this.memory.get_into(sig_data, &mut sig[..]) @@ -711,50 +869,6 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, Ok(if res.is_ok() { 0 } else { 1 }) }, - ext_new_crypto_key(crypto: u32) -> u64 => { - let kind = offchain::CryptoKind::try_from(crypto) - .map_err(|_| "crypto kind OOB while ext_new_crypto_key: wasm")?; - - let res = this.ext.offchain() - .map(|api| api.new_crypto_key(kind)) - .ok_or_else(|| "Calling unavailable API ext_new_crypto_key: wasm")?; - - match res { - Ok(key) => Ok(key.into()), - Err(()) => Ok(u64::max_value()), - } - }, - ext_encrypt( - key: u64, - data: *const u8, - data_len: u32, - msg_len: *mut u32 - ) -> *mut u8 => { - let key = offchain::CryptoKey::try_from(key) - .map_err(|_| "Key OOB while ext_encrypt: wasm")?; - let message = this.memory.get(data, data_len as usize) - .map_err(|_| "OOB while ext_encrypt: wasm")?; - - let res = this.ext.offchain() - .map(|api| api.encrypt(key, &*message)) - .ok_or_else(|| "Calling unavailable API ext_encrypt: wasm")?; - - let (offset,len) = match res { - Ok(encrypted) => { - let len = encrypted.len() as u32; - let offset = this.heap.allocate(len)? as u32; - this.memory.set(offset, &encrypted) - .map_err(|_| "Invalid attempt to set memory in ext_encrypt")?; - (offset, len) - }, - Err(()) => (0, u32::max_value()), - }; - - this.memory.write_primitive(msg_len, len) - .map_err(|_| "Invalid attempt to write msg_len in ext_encrypt")?; - - Ok(offset) - }, ext_network_state(written_out: *mut u32) -> *mut u8 => { let res = this.ext.offchain() .map(|api| api.network_state()) @@ -771,111 +885,6 @@ impl_function_executor!(this: FunctionExecutor<'e, E>, Ok(offset) }, - ext_pubkey( - key: u64, - written_out: *mut u32 - ) -> *mut u8 => { - let key = offchain::CryptoKey::try_from(key) - .map_err(|_| "Key OOB while ext_decrypt: wasm")?; - let res = this.ext.offchain() - .map(|api| api.pubkey(key)) - .ok_or_else(|| "Calling unavailable API ext_authority_pubkey: wasm")?; - - let encoded = res.encode(); - let len = encoded.len() as u32; - let offset = this.heap.allocate(len)? as u32; - this.memory.set(offset, &encoded) - .map_err(|_| "Invalid attempt to set memory in ext_authority_pubkey")?; - this.memory.write_primitive(written_out, len) - .map_err(|_| "Invalid attempt to write written_out in ext_authority_pubkey")?; - Ok(offset) - }, - ext_decrypt( - key: u64, - data: *const u8, - data_len: u32, - msg_len: *mut u32 - ) -> *mut u8 => { - let key = offchain::CryptoKey::try_from(key) - .map_err(|_| "Key OOB while ext_decrypt: wasm")?; - let message = this.memory.get(data, data_len as usize) - .map_err(|_| "OOB while ext_decrypt: wasm")?; - - let res = this.ext.offchain() - .map(|api| api.decrypt(key, &*message)) - .ok_or_else(|| "Calling unavailable API ext_decrypt: wasm")?; - - let (offset,len) = match res { - Ok(decrypted) => { - let len = decrypted.len() as u32; - let offset = this.heap.allocate(len)? as u32; - this.memory.set(offset, &decrypted) - .map_err(|_| "Invalid attempt to set memory in ext_decrypt")?; - (offset, len) - }, - Err(()) => (0, u32::max_value()), - }; - - this.memory.write_primitive(msg_len, len) - .map_err(|_| "Invalid attempt to write msg_len in ext_decrypt")?; - - Ok(offset) - }, - ext_sign( - key: u64, - data: *const u8, - data_len: u32, - sig_data_len: *mut u32 - ) -> *mut u8 => { - let key = offchain::CryptoKey::try_from(key) - .map_err(|_| "Key OOB while ext_sign: wasm")?; - let message = this.memory.get(data, data_len as usize) - .map_err(|_| "OOB while ext_sign: wasm")?; - - let res = this.ext.offchain() - .map(|api| api.sign(key, &*message)) - .ok_or_else(|| "Calling unavailable API ext_sign: wasm")?; - - let (offset,len) = match res { - Ok(signature) => { - let len = signature.len() as u32; - let offset = this.heap.allocate(len)? as u32; - this.memory.set(offset, &signature) - .map_err(|_| "Invalid attempt to set memory in ext_sign")?; - (offset, len) - }, - Err(()) => (0, u32::max_value()), - }; - - this.memory.write_primitive(sig_data_len, len) - .map_err(|_| "Invalid attempt to write sig_data_len in ext_sign")?; - - Ok(offset) - }, - ext_verify( - key: u64, - msg: *const u8, - msg_len: u32, - signature: *const u8, - signature_len: u32 - ) -> u32 => { - let key = offchain::CryptoKey::try_from(key) - .map_err(|_| "Key OOB while ext_verify: wasm")?; - let message = this.memory.get(msg, msg_len as usize) - .map_err(|_| "OOB while ext_verify: wasm")?; - let signature = this.memory.get(signature, signature_len as usize) - .map_err(|_| "OOB while ext_verify: wasm")?; - - let res = this.ext.offchain() - .map(|api| api.verify(key, &*message, &*signature)) - .ok_or_else(|| "Calling unavailable API ext_verify: wasm")?; - - match res { - Ok(true) => Ok(0), - Ok(false) => Ok(1), - Err(()) => Ok(u32::max_value()), - } - }, ext_timestamp() -> u64 => { let timestamp = this.ext.offchain() .map(|api| api.timestamp()) diff --git a/core/executor/src/wasm_utils.rs b/core/executor/src/wasm_utils.rs index 47867f7b4849b..80ef376df5212 100644 --- a/core/executor/src/wasm_utils.rs +++ b/core/executor/src/wasm_utils.rs @@ -217,9 +217,15 @@ macro_rules! dispatch_fn { /// Implements `wasmi::Externals` trait and `Resolver` for given struct. #[macro_export] macro_rules! impl_function_executor { - ( $objectname:ident : $structname:ty, - $( $name:ident ( $( $names:ident : $params:ty ),* ) $( -> $returns:ty )* => $body:tt , )* - => $($pre:tt)+ ) => ( + ( + $objectname:ident : $structname:ty, + $( + $name:ident + ( $( $names:ident : $params:ty ),* $(,)? ) + $( -> $returns:ty )? => { $( $body:tt )* }, + )* + => $( $pre:tt )+ + ) => ( impl $( $pre ) + $structname { #[allow(unused)] fn resolver() -> &'static dyn $crate::wasmi::ModuleImportResolver { @@ -230,7 +236,11 @@ macro_rules! impl_function_executor { name: &str, signature: &$crate::wasmi::Signature ) -> std::result::Result<$crate::wasmi::FuncRef, $crate::wasmi::Error> { - resolve_fn!(signature, name, $( $name( $( $params ),* ) $( -> $returns )* => )*); + resolve_fn!( + signature, + name, + $( $name( $( $params ),* ) $( -> $returns )? => )* + ); Err($crate::wasmi::Error::Instantiation( format!("Export {} not found", name), @@ -249,7 +259,12 @@ macro_rules! impl_function_executor { ) -> std::result::Result, $crate::wasmi::Trap> { let $objectname = self; let mut args = args.as_ref().iter(); - dispatch_fn!(index, $objectname, args, $( $name( $( $names : $params ),* ) $( -> $returns )* => $body ),*); + dispatch_fn! { + index, + $objectname, + args, + $( $name( $( $names : $params ),* ) $( -> $returns )? => { $( $body )* } ),* + }; } } ); diff --git a/core/finality-grandpa/Cargo.toml b/core/finality-grandpa/Cargo.toml index 04f9e7162c943..71a0775cd7e87 100644 --- a/core/finality-grandpa/Cargo.toml +++ b/core/finality-grandpa/Cargo.toml @@ -9,7 +9,7 @@ fork-tree = { path = "../../core/utils/fork-tree" } futures = "0.1" futures03 = { package = "futures-preview", version = "0.3.0-alpha.17", features = ["compat"] } log = "0.4" -parking_lot = "0.8.0" +parking_lot = "0.9.0" tokio-executor = "0.1.7" tokio-timer = "0.2.11" rand = "0.6" @@ -18,6 +18,7 @@ sr-primitives = { path = "../sr-primitives" } consensus_common = { package = "substrate-consensus-common", path = "../consensus/common" } primitives = { package = "substrate-primitives", path = "../primitives" } substrate-telemetry = { path = "../telemetry" } +keystore = { package = "substrate-keystore", path = "../keystore" } serde_json = "1.0" client = { package = "substrate-client", path = "../client" } inherents = { package = "substrate-inherents", path = "../../core/inherents" } @@ -32,8 +33,10 @@ grandpa = { package = "finality-grandpa", version = "0.9.0", features = ["derive network = { package = "substrate-network", path = "../network", features = ["test-helpers"] } keyring = { package = "substrate-keyring", path = "../keyring" } test-client = { package = "substrate-test-runtime-client", path = "../test-runtime/client"} +babe_primitives = { package = "substrate-consensus-babe-primitives", path = "../consensus/babe/primitives" } env_logger = "0.6" tokio = "0.1.17" +tempfile = "3.1" [features] default = ["service-integration"] diff --git a/core/finality-grandpa/primitives/Cargo.toml b/core/finality-grandpa/primitives/Cargo.toml index 1c17639a2a93c..ecbaf2e1ecc70 100644 --- a/core/finality-grandpa/primitives/Cargo.toml +++ b/core/finality-grandpa/primitives/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] client = { package = "substrate-client", path = "../../client", default-features = false } -primitives = { package = "substrate-primitives", path = "../../primitives", default-features = false } +app-crypto = { package = "substrate-application-crypto", path = "../../application-crypto", default-features = false } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } sr-primitives = { path = "../../sr-primitives", default-features = false } rstd = { package = "sr-std", path = "../../sr-std", default-features = false } @@ -15,10 +15,10 @@ serde = { version = "1.0", optional = true, features = ["derive"] } [features] default = ["std"] std = [ - "primitives/std", "client/std", "codec/std", "sr-primitives/std", "rstd/std", "serde", + "app-crypto/std", ] diff --git a/core/finality-grandpa/primitives/src/lib.rs b/core/finality-grandpa/primitives/src/lib.rs index 25f8b802ce02b..b92444e26295c 100644 --- a/core/finality-grandpa/primitives/src/lib.rs +++ b/core/finality-grandpa/primitives/src/lib.rs @@ -28,15 +28,20 @@ use sr_primitives::{ConsensusEngineId, traits::{DigestFor, NumberFor}}; use client::decl_runtime_apis; use rstd::vec::Vec; +mod app { + use app_crypto::{app_crypto, key_types::GRANDPA, ed25519}; + app_crypto!(ed25519, GRANDPA); +} + /// The grandpa crypto scheme defined via the keypair type. #[cfg(feature = "std")] -pub type AuthorityPair = primitives::ed25519::Pair; +pub type AuthorityPair = app::Pair; /// Identity of a Grandpa authority. -pub type AuthorityId = primitives::ed25519::Public; +pub type AuthorityId = app::Public; /// Signature for a Grandpa authority. -pub type AuthoritySignature = primitives::ed25519::Signature; +pub type AuthoritySignature = app::Signature; /// The `ConsensusEngineId` of GRANDPA. pub const GRANDPA_ENGINE_ID: ConsensusEngineId = *b"FRNK"; diff --git a/core/finality-grandpa/src/authorities.rs b/core/finality-grandpa/src/authorities.rs index 27b9c57073b18..9b83c9feb6871 100644 --- a/core/finality-grandpa/src/authorities.rs +++ b/core/finality-grandpa/src/authorities.rs @@ -431,6 +431,7 @@ impl + Clone> PendingChange { #[cfg(test)] mod tests { use super::*; + use primitives::crypto::Public; fn static_is_descendent_of(value: bool) -> impl Fn(&A, &A) -> Result @@ -520,8 +521,8 @@ mod tests { pending_forced_changes: Vec::new(), }; - let set_a = vec![(AuthorityId::from_raw([1; 32]), 5)]; - let set_b = vec![(AuthorityId::from_raw([2; 32]), 5)]; + let set_a = vec![(AuthorityId::from_slice(&[1; 32]), 5)]; + let set_b = vec![(AuthorityId::from_slice(&[2; 32]), 5)]; // two competing changes at the same height on different forks let change_a = PendingChange { @@ -585,8 +586,8 @@ mod tests { pending_forced_changes: Vec::new(), }; - let set_a = vec![(AuthorityId::from_raw([1; 32]), 5)]; - let set_c = vec![(AuthorityId::from_raw([2; 32]), 5)]; + let set_a = vec![(AuthorityId::from_slice(&[1; 32]), 5)]; + let set_c = vec![(AuthorityId::from_slice(&[2; 32]), 5)]; // two competing changes at the same height on different forks let change_a = PendingChange { @@ -651,7 +652,7 @@ mod tests { pending_forced_changes: Vec::new(), }; - let set_a = vec![(AuthorityId::from_raw([1; 32]), 5)]; + let set_a = vec![(AuthorityId::from_slice(&[1; 32]), 5)]; let change_a = PendingChange { next_authorities: set_a.clone(), @@ -717,8 +718,8 @@ mod tests { pending_forced_changes: Vec::new(), }; - let set_a = vec![(AuthorityId::from_raw([1; 32]), 5)]; - let set_b = vec![(AuthorityId::from_raw([2; 32]), 5)]; + let set_a = vec![(AuthorityId::from_slice(&[1; 32]), 5)]; + let set_b = vec![(AuthorityId::from_slice(&[2; 32]), 5)]; let change_a = PendingChange { next_authorities: set_a.clone(), diff --git a/core/finality-grandpa/src/communication/gossip.rs b/core/finality-grandpa/src/communication/gossip.rs index e51e23254cb95..5eb912c95769b 100644 --- a/core/finality-grandpa/src/communication/gossip.rs +++ b/core/finality-grandpa/src/communication/gossip.rs @@ -86,7 +86,7 @@ use sr_primitives::traits::{NumberFor, Block as BlockT, Zero}; use network::consensus_gossip::{self as network_gossip, MessageIntent, ValidatorContext}; use network::{config::Roles, PeerId}; use codec::{Encode, Decode}; -use crate::ed25519::Public as AuthorityId; +use fg_primitives::AuthorityId; use substrate_telemetry::{telemetry, CONSENSUS_DEBUG}; use log::{trace, debug, warn}; @@ -1233,13 +1233,14 @@ mod tests { use super::environment::SharedVoterSetState; use network_gossip::Validator as GossipValidatorT; use network::test::Block; + use primitives::crypto::Public; // some random config (not really needed) fn config() -> crate::Config { crate::Config { gossip_duration: Duration::from_millis(10), justification_period: 256, - local_key: None, + keystore: None, name: None, } } @@ -1452,7 +1453,7 @@ mod tests { voter_set_state(), ); let set_id = 1; - let auth = AuthorityId::from_raw([1u8; 32]); + let auth = AuthorityId::from_slice(&[1u8; 32]); let peer = PeerId::random(); val.note_set(SetId(set_id), vec![auth.clone()], |_, _| {}); @@ -1468,7 +1469,7 @@ mod tests { target_number: 10, }), signature: Default::default(), - id: AuthorityId::from_raw([2u8; 32]), + id: AuthorityId::from_slice(&[2u8; 32]), } }); @@ -1497,7 +1498,7 @@ mod tests { ); let set_id = 1; - let auth = AuthorityId::from_raw([1u8; 32]); + let auth = AuthorityId::from_slice(&[1u8; 32]); let peer = PeerId::random(); val.note_set(SetId(set_id), vec![auth.clone()], |_, _| {}); @@ -1562,7 +1563,7 @@ mod tests { ); let set_id = 1; - let auth = AuthorityId::from_raw([1u8; 32]); + let auth = AuthorityId::from_slice(&[1u8; 32]); let peer = PeerId::random(); val.note_set(SetId(set_id), vec![auth.clone()], |_, _| {}); diff --git a/core/finality-grandpa/src/communication/mod.rs b/core/finality-grandpa/src/communication/mod.rs index 607dba486d2d4..2aa2618535948 100644 --- a/core/finality-grandpa/src/communication/mod.rs +++ b/core/finality-grandpa/src/communication/mod.rs @@ -29,18 +29,18 @@ use std::sync::Arc; -use grandpa::{voter, voter_set::VoterSet}; -use grandpa::Message::{Prevote, Precommit, PrimaryPropose}; use futures::prelude::*; use futures::sync::{oneshot, mpsc}; +use grandpa::Message::{Prevote, Precommit, PrimaryPropose}; +use grandpa::{voter, voter_set::VoterSet}; use log::{debug, trace}; -use tokio_executor::Executor; -use codec::{Encode, Decode}; -use primitives::{ed25519, Pair}; -use substrate_telemetry::{telemetry, CONSENSUS_DEBUG, CONSENSUS_INFO}; -use sr_primitives::traits::{Block as BlockT, Hash as HashT, Header as HeaderT}; use network::{consensus_gossip as network_gossip, NetworkService}; use network_gossip::ConsensusMessage; +use codec::{Encode, Decode}; +use primitives::Pair; +use sr_primitives::traits::{Block as BlockT, Hash as HashT, Header as HeaderT}; +use substrate_telemetry::{telemetry, CONSENSUS_DEBUG, CONSENSUS_INFO}; +use tokio_executor::Executor; use crate::{ CatchUp, Commit, CommunicationIn, CommunicationOut, CompactCommit, Error, @@ -50,7 +50,7 @@ use crate::environment::HasVoted; use gossip::{ GossipMessage, FullCatchUpMessage, FullCommitMessage, VoteOrPrecommitMessage, GossipValidator }; -use primitives::ed25519::{Public as AuthorityId, Signature as AuthoritySignature}; +use fg_primitives::{AuthorityPair, AuthorityId, AuthoritySignature}; pub mod gossip; mod periodic; @@ -341,7 +341,7 @@ impl> NetworkBridge { round: Round, set_id: SetId, voters: Arc>, - local_key: Option>, + local_key: Option, has_voted: HasVoted, ) -> ( impl Stream,Error=Error>, @@ -354,8 +354,7 @@ impl> NetworkBridge { ); let locals = local_key.and_then(|pair| { - let public = pair.public(); - let id = AuthorityId(public.0); + let id = pair.public(); if voters.contains_key(&id) { Some((pair, id)) } else { @@ -633,9 +632,9 @@ pub(crate) fn check_message_sig( round: u64, set_id: u64, ) -> Result<(), ()> { - let as_public = AuthorityId::from_raw(id.0); + let as_public = id.clone(); let encoded_raw = localized_payload(round, set_id, message); - if ed25519::Pair::verify(signature, &encoded_raw, as_public) { + if AuthorityPair::verify(signature, &encoded_raw, &as_public) { Ok(()) } else { debug!(target: "afg", "Bad signature on message from {:?}", id); @@ -653,7 +652,7 @@ pub(crate) fn check_message_sig( struct OutgoingMessages> { round: u64, set_id: u64, - locals: Option<(Arc, AuthorityId)>, + locals: Option<(AuthorityPair, AuthorityId)>, sender: mpsc::UnboundedSender>, network: N, has_voted: HasVoted, diff --git a/core/finality-grandpa/src/communication/tests.rs b/core/finality-grandpa/src/communication/tests.rs index f1473e9863010..70e9413cd9c91 100644 --- a/core/finality-grandpa/src/communication/tests.rs +++ b/core/finality-grandpa/src/communication/tests.rs @@ -133,7 +133,7 @@ fn config() -> crate::Config { crate::Config { gossip_duration: std::time::Duration::from_millis(10), justification_period: 256, - local_key: None, + keystore: None, name: None, } } @@ -204,7 +204,7 @@ fn make_test_network() -> ( fn make_ids(keys: &[Ed25519Keyring]) -> Vec<(AuthorityId, u64)> { keys.iter() - .map(|key| AuthorityId(key.to_raw_public())) + .map(|key| key.clone().public().into()) .map(|id| (id, 1)) .collect() } @@ -242,7 +242,7 @@ fn good_commit_leads_to_relay() { for (i, key) in private.iter().enumerate() { precommits.push(precommit.clone()); - let signature = key.sign(&payload[..]); + let signature = fg_primitives::AuthoritySignature::from(key.sign(&payload[..])); auth_data.push((signature, public[i].0.clone())) } @@ -357,7 +357,7 @@ fn bad_commit_leads_to_report() { for (i, key) in private.iter().enumerate() { precommits.push(precommit.clone()); - let signature = key.sign(&payload[..]); + let signature = fg_primitives::AuthoritySignature::from(key.sign(&payload[..])); auth_data.push((signature, public[i].0.clone())) } diff --git a/core/finality-grandpa/src/environment.rs b/core/finality-grandpa/src/environment.rs index 1949fd25a1fd4..93b176a430edc 100644 --- a/core/finality-grandpa/src/environment.rs +++ b/core/finality-grandpa/src/environment.rs @@ -33,11 +33,11 @@ use grandpa::{ BlockNumberOps, Equivocation, Error as GrandpaError, round::State as RoundState, voter, voter_set::VoterSet, }; +use primitives::{Blake2Hasher, H256, Pair}; use sr_primitives::generic::BlockId; use sr_primitives::traits::{ Block as BlockT, Header as HeaderT, NumberFor, One, Zero, }; -use primitives::{Blake2Hasher, ed25519, H256, Pair}; use substrate_telemetry::{telemetry, CONSENSUS_INFO}; use crate::{ @@ -478,7 +478,7 @@ where { type Timer = Box + Send>; type Id = AuthorityId; - type Signature = ed25519::Signature; + type Signature = AuthoritySignature; // regular round message streams type In = Box { + if local_key.as_ref().map(|k| k.public() == id).unwrap_or(false) { + HasVoted::Yes(id, vote) + } else { + HasVoted::No + } + }, + HasVoted::No => HasVoted::No, + }; let (incoming, outgoing) = self.network.round_communication( crate::communication::Round(round), crate::communication::SetId(self.set_id), self.voters.clone(), - local_key.cloned(), - self.voter_set_state.has_voted(), + local_key.clone(), + has_voted, ); // schedule incoming messages from the network to be held until @@ -523,7 +533,7 @@ where let outgoing = Box::new(outgoing.sink_map_err(Into::into)); voter::RoundData { - voter_id: self.config.local_key.as_ref().map(|pair| pair.public().clone()), + voter_id: local_key.map(|pair| pair.public()), prevote_timer: Box::new(prevote_timer.map_err(|e| Error::Timer(e).into())), precommit_timer: Box::new(precommit_timer.map_err(|e| Error::Timer(e).into())), incoming, @@ -532,12 +542,10 @@ where } fn proposed(&self, _round: u64, propose: PrimaryPropose) -> Result<(), Self::Error> { - let local_id = self.config.local_key.as_ref() - .map(|pair| pair.public().into()) - .filter(|id| self.voters.contains_key(&id)); + let local_id = crate::is_voter(&self.voters, &self.config.keystore); let local_id = match local_id { - Some(id) => id, + Some(id) => id.public(), None => return Ok(()), }; @@ -571,12 +579,10 @@ where } fn prevoted(&self, _round: u64, prevote: Prevote) -> Result<(), Self::Error> { - let local_id = self.config.local_key.as_ref() - .map(|pair| pair.public().into()) - .filter(|id| self.voters.contains_key(&id)); + let local_id = crate::is_voter(&self.voters, &self.config.keystore); let local_id = match local_id { - Some(id) => id, + Some(id) => id.public(), None => return Ok(()), }; @@ -613,12 +619,10 @@ where } fn precommitted(&self, _round: u64, precommit: Precommit) -> Result<(), Self::Error> { - let local_id = self.config.local_key.as_ref() - .map(|pair| pair.public().into()) - .filter(|id| self.voters.contains_key(&id)); + let local_id = crate::is_voter(&self.voters, &self.config.keystore); let local_id = match local_id { - Some(id) => id, + Some(id) => id.public(), None => return Ok(()), }; diff --git a/core/finality-grandpa/src/finality_proof.rs b/core/finality-grandpa/src/finality_proof.rs index 830383ffcae45..6262ad74a7acb 100644 --- a/core/finality-grandpa/src/finality_proof.rs +++ b/core/finality-grandpa/src/finality_proof.rs @@ -582,6 +582,7 @@ pub(crate) mod tests { use test_client::client::{backend::NewBlockState}; use test_client::client::in_mem::Blockchain as InMemoryBlockchain; use super::*; + use primitives::crypto::Public; type FinalityProof = super::FinalityProof

; @@ -740,7 +741,7 @@ pub(crate) mod tests { let proof_of_4 = prove_finality::<_, _, TestJustification>( &blockchain, &( - |_| Ok(vec![(AuthorityId::from_raw([1u8; 32]), 1u64)]), + |_| Ok(vec![(AuthorityId::from_slice(&[1u8; 32]), 1u64)]), |_| unreachable!("authorities didn't change => ProveAuthorities won't be called"), ), 0, @@ -763,7 +764,7 @@ pub(crate) mod tests { let proof_of_5: FinalityProof = Decode::decode(&mut &prove_finality::<_, _, TestJustification>( &blockchain, &( - |_| Ok(vec![(AuthorityId::from_raw([1u8; 32]), 1u64)]), + |_| Ok(vec![(AuthorityId::from_slice(&[1u8; 32]), 1u64)]), |_| unreachable!("should return before calling ProveAuthorities"), ), 0, @@ -789,7 +790,7 @@ pub(crate) mod tests { let proof_of_5: FinalityProof = Decode::decode(&mut &prove_finality::<_, _, TestJustification>( &blockchain, &( - |_| Ok(vec![(AuthorityId::from_raw([1u8; 32]), 1u64)]), + |_| Ok(vec![(AuthorityId::from_slice(&[1u8; 32]), 1u64)]), |_| unreachable!("should return before calling ProveAuthorities"), ), 0, @@ -821,10 +822,12 @@ pub(crate) mod tests { &blockchain, &( |block_id| match block_id { - BlockId::Hash(h) if h == header(3).hash() => Ok(vec![(AuthorityId::from_raw([3u8; 32]), 1u64)]), - BlockId::Number(3) => Ok(vec![(AuthorityId::from_raw([3u8; 32]), 1u64)]), - BlockId::Number(4) => Ok(vec![(AuthorityId::from_raw([4u8; 32]), 1u64)]), - BlockId::Number(6) => Ok(vec![(AuthorityId::from_raw([6u8; 32]), 1u64)]), + BlockId::Hash(h) if h == header(3).hash() => Ok( + vec![(AuthorityId::from_slice(&[3u8; 32]), 1u64)] + ), + BlockId::Number(3) => Ok(vec![(AuthorityId::from_slice(&[3u8; 32]), 1u64)]), + BlockId::Number(4) => Ok(vec![(AuthorityId::from_slice(&[4u8; 32]), 1u64)]), + BlockId::Number(6) => Ok(vec![(AuthorityId::from_slice(&[6u8; 32]), 1u64)]), _ => unreachable!("no other authorities should be fetched: {:?}", block_id), }, |block_id| match block_id { @@ -865,7 +868,7 @@ pub(crate) mod tests { do_check_finality_proof::<_, _, TestJustification>( &blockchain, 1, - vec![(AuthorityId::from_raw([3u8; 32]), 1u64)], + vec![(AuthorityId::from_slice(&[3u8; 32]), 1u64)], &ClosureAuthoritySetForFinalityChecker(|_, _, _| unreachable!("returns before CheckAuthoritiesProof")), vec![42], ).unwrap_err(); @@ -879,7 +882,7 @@ pub(crate) mod tests { do_check_finality_proof::<_, _, TestJustification>( &blockchain, 1, - vec![(AuthorityId::from_raw([3u8; 32]), 1u64)], + vec![(AuthorityId::from_slice(&[3u8; 32]), 1u64)], &ClosureAuthoritySetForFinalityChecker(|_, _, _| unreachable!("returns before CheckAuthoritiesProof")), Vec::::new().encode(), ).unwrap_err(); @@ -893,7 +896,7 @@ pub(crate) mod tests { do_check_finality_proof::<_, _, TestJustification>( &blockchain, 1, - vec![(AuthorityId::from_raw([3u8; 32]), 1u64)], + vec![(AuthorityId::from_slice(&[3u8; 32]), 1u64)], &ClosureAuthoritySetForFinalityChecker(|_, _, _| unreachable!("returns before CheckAuthoritiesProof")), vec![FinalityProofFragment { block: header(4).hash(), @@ -917,7 +920,7 @@ pub(crate) mod tests { do_check_finality_proof::<_, _, TestJustification>( &blockchain, 1, - vec![(AuthorityId::from_raw([3u8; 32]), 1u64)], + vec![(AuthorityId::from_slice(&[3u8; 32]), 1u64)], &ClosureAuthoritySetForFinalityChecker(|_, _, _| unreachable!("returns before CheckAuthoritiesProof")), vec![FinalityProofFragment { block: header(4).hash(), @@ -940,8 +943,8 @@ pub(crate) mod tests { let effects = do_check_finality_proof::<_, _, TestJustification>( &blockchain, 1, - vec![(AuthorityId::from_raw([3u8; 32]), 1u64)], - &ClosureAuthoritySetForFinalityChecker(|_, _, _| Ok(vec![(AuthorityId::from_raw([4u8; 32]), 1u64)])), + vec![(AuthorityId::from_slice(&[3u8; 32]), 1u64)], + &ClosureAuthoritySetForFinalityChecker(|_, _, _| Ok(vec![(AuthorityId::from_slice(&[4u8; 32]), 1u64)])), vec![FinalityProofFragment { block: header(2).hash(), justification: TestJustification(true, vec![7]).encode(), @@ -959,7 +962,7 @@ pub(crate) mod tests { block: header(4).hash(), justification: TestJustification(true, vec![8]).encode(), new_set_id: 2, - new_authorities: vec![(AuthorityId::from_raw([4u8; 32]), 1u64)], + new_authorities: vec![(AuthorityId::from_slice(&[4u8; 32]), 1u64)], }); } @@ -976,7 +979,7 @@ pub(crate) mod tests { let proof_of_4 = prove_finality::<_, _, TestJustification>( &blockchain, &( - |_| Ok(vec![(AuthorityId::from_raw([1u8; 32]), 1u64)]), + |_| Ok(vec![(AuthorityId::from_slice(&[1u8; 32]), 1u64)]), |_| unreachable!("should return before calling ProveAuthorities"), ), 0, diff --git a/core/finality-grandpa/src/lib.rs b/core/finality-grandpa/src/lib.rs index 0d04e964e2e17..a0df661f0cf3d 100644 --- a/core/finality-grandpa/src/lib.rs +++ b/core/finality-grandpa/src/lib.rs @@ -55,17 +55,20 @@ use futures::prelude::*; use log::{debug, info, warn}; use futures::sync::mpsc; -use client::{BlockchainEvents, CallExecutor, Client, backend::Backend, error::Error as ClientError}; +use client::{ + BlockchainEvents, CallExecutor, Client, backend::Backend, error::Error as ClientError, +}; use client::blockchain::HeaderBackend; use codec::Encode; +use sr_primitives::generic::BlockId; use sr_primitives::traits::{ - NumberFor, Block as BlockT, DigestFor, ProvideRuntimeApi, + NumberFor, Block as BlockT, DigestFor, ProvideRuntimeApi }; -use fg_primitives::GrandpaApi; +use fg_primitives::{GrandpaApi, AuthorityPair}; +use keystore::KeyStorePtr; use inherents::InherentDataProviders; -use sr_primitives::generic::BlockId; use consensus_common::SelectChain; -use primitives::{ed25519, H256, Pair, Blake2Hasher}; +use primitives::{H256, Blake2Hasher}; use substrate_telemetry::{telemetry, CONSENSUS_INFO, CONSENSUS_DEBUG, CONSENSUS_WARN}; use serde_json; @@ -198,10 +201,10 @@ pub struct Config { /// at least every justification_period blocks. There are some other events which might cause /// justification generation. pub justification_period: u32, - /// The local signing key. - pub local_key: Option>, /// Some local identifier of the voter. pub name: Option, + /// The keystore that manages the keys of this node. + pub keystore: Option, } impl Config { @@ -396,11 +399,11 @@ where } fn global_communication, B, E, N, RA>( - local_key: Option<&Arc>, set_id: u64, voters: &Arc>, client: &Arc>, network: &NetworkBridge, + keystore: &Option, ) -> ( impl Stream< Item = CommunicationInH, @@ -417,10 +420,7 @@ fn global_communication, B, E, N, RA>( RA: Send + Sync, NumberFor: BlockNumberOps, { - - let is_voter = local_key - .map(|pair| voters.contains_key(&pair.public().into())) - .unwrap_or(false); + let is_voter = is_voter(voters, keystore).is_some(); // verification stream let (global_in, global_out) = network.global_communication( @@ -491,7 +491,7 @@ pub struct GrandpaParams, N, RA, SC, X> { /// block import worker that has already been instantiated with `block_import`. pub fn run_grandpa_voter, N, RA, SC, X>( grandpa_params: GrandpaParams, -) -> ::client::error::Result + Send + 'static> where +) -> client::error::Result + Send + 'static> where Block::Hash: Ord, B: Backend + 'static, E: CallExecutor + Send + Sync + 'static, @@ -568,31 +568,6 @@ pub fn run_grandpa_voter, N, RA, SC, X>( voter_set_state: set_state.clone(), }); - initial_environment.update_voter_set_state(|voter_set_state| { - match voter_set_state { - VoterSetState::Live { current_round: HasVoted::Yes(id, _), completed_rounds } => { - let local_id = config.local_key.clone().map(|pair| pair.public()); - let has_voted = match local_id { - Some(local_id) => if *id == local_id { - // keep the previous votes - return Ok(None); - } else { - HasVoted::No - }, - _ => HasVoted::No, - }; - - // NOTE: only updated on disk when the voter first - // proposes/prevotes/precommits or completes a round. - Ok(Some(VoterSetState::Live { - current_round: has_voted, - completed_rounds: completed_rounds.clone(), - })) - }, - _ => Ok(None), - } - }).expect("operation inside closure cannot fail; qed"); - let initial_state = (initial_environment, voter_commands_rx.into_future()); let voter_work = future::loop_fn(initial_state, move |params| { let (env, voter_commands_rx) = params; @@ -611,20 +586,18 @@ pub fn run_grandpa_voter, N, RA, SC, X>( ); let global_comms = global_communication( - config.local_key.as_ref(), env.set_id, &env.voters, &client, &network, + &config.keystore, ); - let voters = (*env.voters).clone(); - let last_completed_round = completed_rounds.last(); Some(voter::Voter::new( env.clone(), - voters, + (*env.voters).clone(), global_comms, last_completed_round.number, last_completed_round.state.clone(), @@ -777,3 +750,17 @@ pub fn run_grandpa, N, RA, SC, X>( { run_grandpa_voter(grandpa_params) } + +/// Checks if this node is a voter in the given voter set. +/// +/// Returns the key pair of the node that is being used in the current voter set or `None`. +fn is_voter( + voters: &Arc>, + keystore: &Option, +) -> Option { + match keystore { + Some(keystore) => voters.voters().iter() + .find_map(|(p, _)| keystore.read().key_pair::(&p).ok()), + None => None, + } +} \ No newline at end of file diff --git a/core/finality-grandpa/src/light_import.rs b/core/finality-grandpa/src/light_import.rs index 3fec523dcb6d9..4dc934303ca4d 100644 --- a/core/finality-grandpa/src/light_import.rs +++ b/core/finality-grandpa/src/light_import.rs @@ -538,7 +538,7 @@ fn on_post_finalization_error(error: ClientError, value_type: &str) -> Consensus pub mod tests { use super::*; use consensus_common::ForkChoiceStrategy; - use primitives::H256; + use primitives::{H256, crypto::Public}; use test_client::client::in_mem::Blockchain as InMemoryAuxStore; use test_client::runtime::{Block, Header}; use crate::tests::TestApi; @@ -637,7 +637,7 @@ pub mod tests { let client = test_client::new_light(); let mut import_data = LightImportData { last_finalized: Default::default(), - authority_set: LightAuthoritySet::genesis(vec![(AuthorityId::from_raw([1; 32]), 1)]), + authority_set: LightAuthoritySet::genesis(vec![(AuthorityId::from_slice(&[1; 32]), 1)]), consensus_changes: ConsensusChanges::empty(), }; let block = BlockImportParams { @@ -688,7 +688,7 @@ pub mod tests { #[test] fn finality_proof_required_when_consensus_data_changes_and_no_justification_provided() { let mut cache = HashMap::new(); - cache.insert(well_known_cache_keys::AUTHORITIES, vec![AuthorityId::from_raw([2; 32])].encode()); + cache.insert(well_known_cache_keys::AUTHORITIES, vec![AuthorityId::from_slice(&[2; 32])].encode()); assert_eq!(import_block(cache, None), ImportResult::Imported(ImportedAux { clear_justification_requests: false, needs_justification: false, @@ -701,7 +701,7 @@ pub mod tests { fn finality_proof_required_when_consensus_data_changes_and_incorrect_justification_provided() { let justification = TestJustification(false, Vec::new()).encode(); let mut cache = HashMap::new(); - cache.insert(well_known_cache_keys::AUTHORITIES, vec![AuthorityId::from_raw([2; 32])].encode()); + cache.insert(well_known_cache_keys::AUTHORITIES, vec![AuthorityId::from_slice(&[2; 32])].encode()); assert_eq!( import_block(cache, Some(justification)), ImportResult::Imported(ImportedAux { @@ -717,7 +717,7 @@ pub mod tests { #[test] fn aux_data_updated_on_start() { let aux_store = InMemoryAuxStore::::new(); - let api = Arc::new(TestApi::new(vec![(AuthorityId::from_raw([1; 32]), 1)])); + let api = Arc::new(TestApi::new(vec![(AuthorityId::from_slice(&[1; 32]), 1)])); // when aux store is empty initially assert!(aux_store.get_aux(LIGHT_AUTHORITY_SET_KEY).unwrap().is_none()); @@ -732,7 +732,7 @@ pub mod tests { #[test] fn aux_data_loaded_on_restart() { let aux_store = InMemoryAuxStore::::new(); - let api = Arc::new(TestApi::new(vec![(AuthorityId::from_raw([1; 32]), 1)])); + let api = Arc::new(TestApi::new(vec![(AuthorityId::from_slice(&[1; 32]), 1)])); // when aux store is non-empty initially let mut consensus_changes = ConsensusChanges::::empty(); @@ -741,7 +741,9 @@ pub mod tests { &[ ( LIGHT_AUTHORITY_SET_KEY, - LightAuthoritySet::genesis(vec![(AuthorityId::from_raw([42; 32]), 2)]).encode().as_slice(), + LightAuthoritySet::genesis( + vec![(AuthorityId::from_slice(&[42; 32]), 2)] + ).encode().as_slice(), ), ( LIGHT_CONSENSUS_CHANGES_KEY, @@ -753,7 +755,7 @@ pub mod tests { // importer uses it on start let data = load_aux_import_data(Default::default(), &aux_store, api).unwrap(); - assert_eq!(data.authority_set.authorities(), vec![(AuthorityId::from_raw([42; 32]), 2)]); + assert_eq!(data.authority_set.authorities(), vec![(AuthorityId::from_slice(&[42; 32]), 2)]); assert_eq!(data.consensus_changes.pending_changes(), &[(42, Default::default())]); } } diff --git a/core/finality-grandpa/src/observer.rs b/core/finality-grandpa/src/observer.rs index 1301a6656bba7..f347b678b6cef 100644 --- a/core/finality-grandpa/src/observer.rs +++ b/core/finality-grandpa/src/observer.rs @@ -185,11 +185,11 @@ pub fn run_grandpa_observer, N, RA, SC>( // start global communication stream for the current set let (global_in, _) = global_communication( - None, set_id, &voters, &client, &network, + &config.keystore, ); let last_finalized_number = client.info().chain.finalized_number; diff --git a/core/finality-grandpa/src/tests.rs b/core/finality-grandpa/src/tests.rs index 38a5dd5441473..8b0cc3bc0ca64 100644 --- a/core/finality-grandpa/src/tests.rs +++ b/core/finality-grandpa/src/tests.rs @@ -37,7 +37,7 @@ use std::result; use codec::Decode; use sr_primitives::traits::{ApiRef, ProvideRuntimeApi, Header as HeaderT}; use sr_primitives::generic::BlockId; -use primitives::{NativeOrEncoded, ExecutionContext}; +use primitives::{NativeOrEncoded, ExecutionContext, crypto::Public}; use fg_primitives::AuthorityId; use authorities::AuthoritySet; @@ -273,7 +273,7 @@ impl GrandpaApi for RuntimeApi { _: ExecutionContext, _: Option<()>, _: Vec, - ) -> Result>> { + ) -> Result>> { Ok(self.inner.genesis_authorities.clone()).map(NativeOrEncoded::Native) } @@ -342,11 +342,17 @@ impl AuthoritySetForFinalityChecker for TestApi { const TEST_GOSSIP_DURATION: Duration = Duration::from_millis(500); -fn make_ids(keys: &[Ed25519Keyring]) -> Vec<(primitives::ed25519::Public, u64)> { - keys.iter() - .map(|key| AuthorityId::from_raw(key.to_raw_public())) - .map(|id| (id, 1)) - .collect() +fn make_ids(keys: &[Ed25519Keyring]) -> Vec<(AuthorityId, u64)> { + keys.iter().map(|key| key.clone().public().into()).map(|id| (id, 1)).collect() +} + +fn create_keystore(authority: Ed25519Keyring) -> (KeyStorePtr, tempfile::TempDir) { + let keystore_path = tempfile::tempdir().expect("Creates keystore path"); + let keystore = keystore::Store::open(keystore_path.path(), None).expect("Creates keystore"); + keystore.write().insert_ephemeral_from_seed::(&authority.to_seed()) + .expect("Creates authority key"); + + (keystore, keystore_path) } // run the voters to completion. provide a closure to be invoked after @@ -370,7 +376,11 @@ fn run_to_completion_with( wait_for.push(f); }; + let mut keystore_paths = Vec::new(); for (peer_id, key) in peers.iter().enumerate() { + let (keystore, keystore_path) = create_keystore(*key); + keystore_paths.push(keystore_path); + let highest_finalized = highest_finalized.clone(); let (client, net_service, link) = { let net = net.lock(); @@ -405,7 +415,7 @@ fn run_to_completion_with( config: Config { gossip_duration: TEST_GOSSIP_DURATION, justification_period: 32, - local_key: Some(Arc::new(key.clone().into())), + keystore: Some(keystore), name: Some(format!("peer#{}", peer_id)), }, link: link, @@ -462,8 +472,10 @@ fn finalize_3_voters_no_observers() { run_to_completion(&mut runtime, 20, net.clone(), peers); // normally there's no justification for finalized blocks - assert!(net.lock().peer(0).client().justification(&BlockId::Number(20)).unwrap().is_none(), - "Extra justification for block#1"); + assert!( + net.lock().peer(0).client().justification(&BlockId::Number(20)).unwrap().is_none(), + "Extra justification for block#1", + ); } #[test] @@ -482,8 +494,10 @@ fn finalize_3_voters_1_full_observer() { let all_peers = peers.iter() .cloned() - .map(|key| Some(Arc::new(key.into()))) - .chain(::std::iter::once(None)); + .map(Some) + .chain(std::iter::once(None)); + + let mut keystore_paths = Vec::new(); for (peer_id, local_key) in all_peers.enumerate() { let (client, net_service, link) = { @@ -502,11 +516,19 @@ fn finalize_3_voters_1_full_observer() { .for_each(move |_| Ok(())) ); + let keystore = if let Some(local_key) = local_key { + let (keystore, keystore_path) = create_keystore(local_key); + keystore_paths.push(keystore_path); + Some(keystore) + } else { + None + }; + let grandpa_params = GrandpaParams { config: Config { gossip_duration: TEST_GOSSIP_DURATION, justification_period: 32, - local_key, + keystore, name: Some(format!("peer#{}", peer_id)), }, link: link, @@ -521,7 +543,7 @@ fn finalize_3_voters_1_full_observer() { } // wait for all finalized on each. - let wait_for = ::futures::future::join_all(finality_notifications) + let wait_for = futures::future::join_all(finality_notifications) .map(|_| ()) .map_err(|_| ()); @@ -640,10 +662,13 @@ fn transition_3_voters_twice_1_full_observer() { .cloned() .collect::>() // deduplicate .into_iter() - .map(|key| Some(Arc::new(key.into()))) .enumerate(); + let mut keystore_paths = Vec::new(); for (peer_id, local_key) in all_peers { + let (keystore, keystore_path) = create_keystore(local_key); + keystore_paths.push(keystore_path); + let (client, net_service, link) = { let net = net.lock(); let link = net.peers[peer_id].data.lock().take().expect("link initialized at startup; qed"); @@ -674,7 +699,7 @@ fn transition_3_voters_twice_1_full_observer() { config: Config { gossip_duration: TEST_GOSSIP_DURATION, justification_period: 32, - local_key, + keystore: Some(keystore), name: Some(format!("peer#{}", peer_id)), }, link: link, @@ -704,7 +729,7 @@ fn justification_is_emitted_when_consensus_data_changes() { let mut net = GrandpaTestNet::new(TestApi::new(make_ids(peers)), 3); // import block#1 WITH consensus data change - let new_authorities = vec![primitives::sr25519::Public::from_raw([42; 32])]; + let new_authorities = vec![babe_primitives::AuthorityId::from_slice(&[42; 32])]; net.peer(0).push_authorities_change_block(new_authorities); net.block_until_sync(&mut runtime); let net = Arc::new(Mutex::new(net)); @@ -1080,6 +1105,8 @@ fn voter_persists_its_votes() { let (voter_tx, voter_rx) = mpsc::unbounded::<()>(); + let mut keystore_paths = Vec::new(); + // startup a grandpa voter for alice but also listen for messages on a // channel. whenever a message is received the voter is restarted. when the // sender is dropped the voter is stopped. @@ -1087,6 +1114,9 @@ fn voter_persists_its_votes() { let net = net.clone(); let client = client.clone(); + let (keystore, keystore_path) = create_keystore(peers[0]); + keystore_paths.push(keystore_path); + let voter = future::loop_fn(voter_rx, move |rx| { let (_block_import, _, _, _, link) = net.lock().make_block_import(client.clone()); let link = link.lock().take().unwrap(); @@ -1095,10 +1125,10 @@ fn voter_persists_its_votes() { config: Config { gossip_duration: TEST_GOSSIP_DURATION, justification_period: 32, - local_key: Some(Arc::new(peers[0].clone().into())), + keystore: Some(keystore.clone()), name: Some(format!("peer#{}", 0)), }, - link: link, + link, network: net.lock().peers[0].network_service().clone(), inherent_data_providers: InherentDataProviders::new(), on_exit: Exit, @@ -1144,10 +1174,13 @@ fn voter_persists_its_votes() { // voter. instead we'll listen for the prevote that alice casts // and cast our own manually { + let (keystore, keystore_path) = create_keystore(peers[1]); + keystore_paths.push(keystore_path); + let config = Config { gossip_duration: TEST_GOSSIP_DURATION, justification_period: 32, - local_key: Some(Arc::new(peers[1].clone().into())), + keystore: Some(keystore), name: Some(format!("peer#{}", 1)), }; @@ -1170,7 +1203,7 @@ fn voter_persists_its_votes() { communication::Round(1), communication::SetId(0), Arc::new(VoterSet::from_iter(voters)), - Some(config.local_key.unwrap()), + Some(peers[1].pair().into()), HasVoted::No, ); @@ -1296,7 +1329,7 @@ fn finalize_3_voters_1_light_observer() { Config { gossip_duration: TEST_GOSSIP_DURATION, justification_period: 32, - local_key: None, + keystore: None, name: Some("observer".to_string()), }, link, @@ -1320,7 +1353,7 @@ fn finality_proof_is_fetched_by_light_client_when_consensus_data_changes() { // import block#1 WITH consensus data change. Light client ignores justification // && instead fetches finality proof for block #1 - net.peer(0).push_authorities_change_block(vec![primitives::sr25519::Public::from_raw([42; 32])]); + net.peer(0).push_authorities_change_block(vec![babe_primitives::AuthorityId::from_slice(&[42; 32])]); let net = Arc::new(Mutex::new(net)); run_to_completion(&mut runtime, 1, net.clone(), peers); net.lock().block_until_sync(&mut runtime); @@ -1383,7 +1416,7 @@ fn empty_finality_proof_is_returned_to_light_client_when_authority_set_is_differ // normally it will reach light client, but because of the forced change, it will not net.lock().peer(0).push_blocks(8, false); // best is #9 net.lock().peer(0).push_authorities_change_block( - vec![primitives::sr25519::Public::from_raw([42; 32])] + vec![babe_primitives::AuthorityId::from_slice(&[42; 32])] ); // #10 net.lock().peer(0).push_blocks(1, false); // best is #11 net.lock().block_until_sync(&mut runtime); @@ -1417,15 +1450,15 @@ fn voter_catches_up_to_latest_round_when_behind() { let net = Arc::new(Mutex::new(net)); let mut finality_notifications = Vec::new(); - let voter = |local_key, peer_id, link, net: Arc>| -> Box + Send> { + let voter = |keystore, peer_id, link, net: Arc>| -> Box + Send> { let grandpa_params = GrandpaParams { config: Config { gossip_duration: TEST_GOSSIP_DURATION, justification_period: 32, - local_key, + keystore, name: Some(format!("peer#{}", peer_id)), }, - link: link, + link, network: net.lock().peer(peer_id).network_service().clone(), inherent_data_providers: InherentDataProviders::new(), on_exit: Exit, @@ -1435,6 +1468,8 @@ fn voter_catches_up_to_latest_round_when_behind() { Box::new(run_grandpa_voter(grandpa_params).expect("all in order with client and network")) }; + let mut keystore_paths = Vec::new(); + // spawn authorities for (peer_id, key) in peers.iter().enumerate() { let (client, link) = { @@ -1453,7 +1488,10 @@ fn voter_catches_up_to_latest_round_when_behind() { .for_each(move |_| Ok(())) ); - let voter = voter(Some(Arc::new((*key).into())), peer_id, link, net.clone()); + let (keystore, keystore_path) = create_keystore(*key); + keystore_paths.push(keystore_path); + + let voter = voter(Some(keystore), peer_id, link, net.clone()); runtime.spawn(voter); } diff --git a/core/inherents/Cargo.toml b/core/inherents/Cargo.toml index f89288578ac8c..45e0b9e828ec7 100644 --- a/core/inherents/Cargo.toml +++ b/core/inherents/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -parking_lot = { version = "0.8.0", optional = true } +parking_lot = { version = "0.9.0", optional = true } rstd = { package = "sr-std", path = "../sr-std", default-features = false } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } sr-primitives = { path = "../sr-primitives", default-features = false } diff --git a/core/keyring/src/ed25519.rs b/core/keyring/src/ed25519.rs index a66cc73b56466..56bdb1ce8c0e6 100644 --- a/core/keyring/src/ed25519.rs +++ b/core/keyring/src/ed25519.rs @@ -36,18 +36,7 @@ pub enum Keyring { impl Keyring { pub fn from_public(who: &Public) -> Option { - [ - Keyring::Alice, - Keyring::Bob, - Keyring::Charlie, - Keyring::Dave, - Keyring::Eve, - Keyring::Ferdie, - Keyring::One, - Keyring::Two, - ].iter() - .map(|i| *i) - .find(|&k| &Public::from(k) == who) + Self::iter().find(|&k| &Public::from(k) == who) } pub fn from_raw_public(who: [u8; 32]) -> Option { @@ -83,6 +72,14 @@ impl Keyring { pub fn iter() -> impl Iterator { ::iter() } + + pub fn public(self) -> Public { + self.pair().public() + } + + pub fn to_seed(self) -> String { + format!("//{}", self) + } } impl From for &'static str { @@ -172,8 +169,26 @@ mod tests { #[test] fn should_work() { - assert!(Pair::verify(&Keyring::Alice.sign(b"I am Alice!"), b"I am Alice!", Keyring::Alice)); - assert!(!Pair::verify(&Keyring::Alice.sign(b"I am Alice!"), b"I am Bob!", Keyring::Alice)); - assert!(!Pair::verify(&Keyring::Alice.sign(b"I am Alice!"), b"I am Alice!", Keyring::Bob)); + assert!( + Pair::verify( + &Keyring::Alice.sign(b"I am Alice!"), + b"I am Alice!", + &Keyring::Alice.public(), + ) + ); + assert!( + !Pair::verify( + &Keyring::Alice.sign(b"I am Alice!"), + b"I am Bob!", + &Keyring::Alice.public(), + ) + ); + assert!( + !Pair::verify( + &Keyring::Alice.sign(b"I am Alice!"), + b"I am Alice!", + &Keyring::Bob.public(), + ) + ); } } diff --git a/core/keyring/src/sr25519.rs b/core/keyring/src/sr25519.rs index aefdc05fa7119..bb3aaa6b51db1 100644 --- a/core/keyring/src/sr25519.rs +++ b/core/keyring/src/sr25519.rs @@ -37,18 +37,7 @@ pub enum Keyring { impl Keyring { pub fn from_public(who: &Public) -> Option { - [ - Keyring::Alice, - Keyring::Bob, - Keyring::Charlie, - Keyring::Dave, - Keyring::Eve, - Keyring::Ferdie, - Keyring::One, - Keyring::Two, - ].iter() - .map(|i| *i) - .find(|&k| &Public::from(k) == who) + Self::iter().find(|&k| &Public::from(k) == who) } pub fn from_raw_public(who: [u8; 32]) -> Option { @@ -84,6 +73,14 @@ impl Keyring { pub fn iter() -> impl Iterator { ::iter() } + + pub fn public(self) -> Public { + self.pair().public() + } + + pub fn to_seed(self) -> String { + format!("//{}", self) + } } impl From for &'static str { @@ -109,16 +106,7 @@ impl From for sr_primitives::MultiSigner { lazy_static! { static ref PRIVATE_KEYS: HashMap = { - [ - Keyring::Alice, - Keyring::Bob, - Keyring::Charlie, - Keyring::Dave, - Keyring::Eve, - Keyring::Ferdie, - Keyring::One, - Keyring::Two, - ].iter().map(|&i| (i, i.pair())).collect() + Keyring::iter().map(|i| (i, i.pair())).collect() }; static ref PUBLIC_KEYS: HashMap = { @@ -182,8 +170,26 @@ mod tests { #[test] fn should_work() { - assert!(Pair::verify(&Keyring::Alice.sign(b"I am Alice!"), b"I am Alice!", Keyring::Alice)); - assert!(!Pair::verify(&Keyring::Alice.sign(b"I am Alice!"), b"I am Bob!", Keyring::Alice)); - assert!(!Pair::verify(&Keyring::Alice.sign(b"I am Alice!"), b"I am Alice!", Keyring::Bob)); + assert!( + Pair::verify( + &Keyring::Alice.sign(b"I am Alice!"), + b"I am Alice!", + &Keyring::Alice.public(), + ) + ); + assert!( + !Pair::verify( + &Keyring::Alice.sign(b"I am Alice!"), + b"I am Bob!", + &Keyring::Alice.public(), + ) + ); + assert!( + !Pair::verify( + &Keyring::Alice.sign(b"I am Alice!"), + b"I am Alice!", + &Keyring::Bob.public(), + ) + ); } } diff --git a/core/keystore/Cargo.toml b/core/keystore/Cargo.toml index 00a653c114168..c56b9ba67dcbd 100644 --- a/core/keystore/Cargo.toml +++ b/core/keystore/Cargo.toml @@ -5,12 +5,14 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -derive_more = "0.14.0" +derive_more = "0.15.0" primitives = { package = "substrate-primitives", path = "../primitives" } +app-crypto = { package = "substrate-application-crypto", path = "../application-crypto" } hex = "0.3" rand = "0.6" serde_json = "1.0" subtle = "2.0" +parking_lot = "0.9.0" [dev-dependencies] tempdir = "0.3" diff --git a/core/keystore/src/lib.rs b/core/keystore/src/lib.rs index 89cfca559c650..5a6df04b77e65 100644 --- a/core/keystore/src/lib.rs +++ b/core/keystore/src/lib.rs @@ -18,12 +18,18 @@ #![warn(missing_docs)] -use std::collections::HashMap; -use std::path::PathBuf; -use std::fs::{self, File}; -use std::io::{self, Write}; +use std::{collections::HashMap, path::PathBuf, fs::{self, File}, io::{self, Write}, sync::Arc}; -use primitives::crypto::{KeyTypeId, Pair, Public}; +use primitives::{ + crypto::{KeyTypeId, Pair as PairT, Public, IsWrappedBy, Protected}, traits::BareCryptoStore, +}; + +use app_crypto::{AppKey, AppPublic, AppPair, ed25519, sr25519}; + +use parking_lot::RwLock; + +/// Keystore pointer +pub type KeyStorePtr = Arc>; /// Keystore error. #[derive(Debug, derive_more::Display, derive_more::From)] @@ -41,6 +47,9 @@ pub enum Error { /// Invalid seed #[display(fmt="Invalid seed")] InvalidSeed, + /// Keystore unavailable + #[display(fmt="Keystore unavailable")] + Unavailable, } /// Keystore Result @@ -57,80 +66,159 @@ impl std::error::Error for Error { } /// Key store. +/// +/// Stores key pairs in a file system store + short lived key pairs in memory. +/// +/// Every pair that is being generated by a `seed`, will be placed in memory. pub struct Store { path: PathBuf, additional: HashMap<(KeyTypeId, Vec), Vec>, + password: Option>, } impl Store { - /// Create a new store at the given path. - pub fn open(path: PathBuf) -> Result { + /// Open the store at the given path. + /// + /// Optionally takes a password that will be used to encrypt/decrypt the keys. + pub fn open>(path: T, password: Option>) -> Result { + let path = path.into(); fs::create_dir_all(&path)?; - Ok(Store { path, additional: HashMap::new() }) + + let instance = Self { path, additional: HashMap::new(), password }; + Ok(Arc::new(RwLock::new(instance))) } - fn get_pair(&self, public: &TPair::Public) -> Result> { - let key = (TPair::KEY_TYPE, public.to_raw_vec()); - if let Some(bytes) = self.additional.get(&key) { - let pair = TPair::from_seed_slice(bytes) - .map_err(|_| Error::InvalidSeed)?; - return Ok(Some(pair)); - } - Ok(None) + /// Get the public/private key pair for the given public key and key type. + fn get_additional_pair( + &self, + public: &Pair::Public, + key_type: KeyTypeId, + ) -> Result> { + let key = (key_type, public.to_raw_vec()); + self.additional + .get(&key) + .map(|bytes| Pair::from_seed_slice(bytes).map_err(|_| Error::InvalidSeed)) + .transpose() } - fn insert_pair(&mut self, pair: &TPair) { - let key = (TPair::KEY_TYPE, pair.public().to_raw_vec()); + /// Insert the given public/private key pair with the given key type. + /// + /// Does not place it into the file system store. + fn insert_ephemeral_pair(&mut self, pair: &Pair, key_type: KeyTypeId) { + let key = (key_type, pair.public().to_raw_vec()); self.additional.insert(key, pair.to_raw_vec()); } - /// Generate a new key, placing it into the store. - pub fn generate(&self, password: &str) -> Result { - let (pair, phrase, _) = TPair::generate_with_phrase(Some(password)); - let mut file = File::create(self.key_file_path::(&pair.public()))?; - ::serde_json::to_writer(&file, &phrase)?; + /// Insert a new key with anonymous crypto. + /// + /// Places it into the file system store. + fn insert_unknown(&self, key_type: KeyTypeId, suri: &str, public: &[u8]) -> Result<()> { + let mut file = File::create(self.key_file_path(public, key_type)).map_err(Error::Io)?; + serde_json::to_writer(&file, &suri).map_err(Error::Json)?; + file.flush().map_err(Error::Io)?; + Ok(()) + } + + /// Insert a new key. + /// + /// Places it into the file system store. + pub fn insert_by_type(&self, key_type: KeyTypeId, suri: &str) -> Result { + let pair = Pair::from_string( + suri, + self.password.as_ref().map(|p| &***p) + ).map_err(|_| Error::InvalidSeed)?; + self.insert_unknown(key_type, suri, pair.public().as_slice()) + .map_err(|_| Error::Unavailable)?; + Ok(pair) + } + + /// Insert a new key. + /// + /// Places it into the file system store. + pub fn insert(&self, suri: &str) -> Result { + self.insert_by_type::(Pair::ID, suri).map(Into::into) + } + + /// Generate a new key. + /// + /// Places it into the file system store. + pub fn generate_by_type(&self, key_type: KeyTypeId) -> Result { + let (pair, phrase, _) = Pair::generate_with_phrase(self.password.as_ref().map(|p| &***p)); + let mut file = File::create(self.key_file_path(pair.public().as_slice(), key_type))?; + serde_json::to_writer(&file, &phrase)?; file.flush()?; Ok(pair) } - /// Create a new key from seed. Do not place it into the store. - pub fn generate_from_seed(&mut self, seed: &str) -> Result { - let pair = TPair::from_string(seed, None) - .ok().ok_or(Error::InvalidSeed)?; - self.insert_pair(&pair); + /// Generate a new key. + /// + /// Places it into the file system store. + pub fn generate(&self) -> Result { + self.generate_by_type::(Pair::ID).map(Into::into) + } + + /// Create a new key from seed. + /// + /// Does not place it into the file system store. + pub fn insert_ephemeral_from_seed_by_type( + &mut self, + seed: &str, + key_type: KeyTypeId, + ) -> Result { + let pair = Pair::from_string(seed, None).map_err(|_| Error::InvalidSeed)?; + self.insert_ephemeral_pair(&pair, key_type); Ok(pair) } - /// Load a key file with given public key. - pub fn load(&self, public: &TPair::Public, password: &str) -> Result { - if let Some(pair) = self.get_pair(public)? { + /// Create a new key from seed. + /// + /// Does not place it into the file system store. + pub fn insert_ephemeral_from_seed(&mut self, seed: &str) -> Result { + self.insert_ephemeral_from_seed_by_type::(seed, Pair::ID).map(Into::into) + } + + /// Get a key pair for the given public key and key type. + pub fn key_pair_by_type(&self, + public: &Pair::Public, + key_type: KeyTypeId, + ) -> Result { + if let Some(pair) = self.get_additional_pair(public, key_type)? { return Ok(pair) } - let path = self.key_file_path::(public); + let path = self.key_file_path(public.as_slice(), key_type); let file = File::open(path)?; - let phrase: String = ::serde_json::from_reader(&file)?; - let (pair, _) = TPair::from_phrase(&phrase, Some(password)) - .ok().ok_or(Error::InvalidPhrase)?; - if &pair.public() != public { - return Err(Error::InvalidPassword); + let phrase: String = serde_json::from_reader(&file)?; + let pair = Pair::from_phrase( + &phrase, + self.password.as_ref().map(|p| &***p), + ).map_err(|_| Error::InvalidPhrase)?.0; + + if &pair.public() == public { + Ok(pair) + } else { + Err(Error::InvalidPassword) } - Ok(pair) } - /// Get public keys of all stored keys. - pub fn contents(&self) -> Result> { + /// Get a key pair for the given public key. + pub fn key_pair(&self, public: &::Public) -> Result { + self.key_pair_by_type::(IsWrappedBy::from_ref(public), Pair::ID).map(Into::into) + } + + /// Get public keys of all stored keys that match the given key type. + pub fn public_keys_by_type(&self, key_type: KeyTypeId) -> Result> { let mut public_keys: Vec = self.additional.keys() .filter_map(|(ty, public)| { - if *ty != TPublic::KEY_TYPE { - return None + if *ty == key_type { + Some(TPublic::from_slice(public)) + } else { + None } - Some(TPublic::from_slice(public)) }) .collect(); - let key_type: [u8; 4] = TPublic::KEY_TYPE.to_le_bytes(); for entry in fs::read_dir(&self.path)? { let entry = entry?; let path = entry.path(); @@ -139,7 +227,7 @@ impl Store { if let Some(name) = path.file_name().and_then(|n| n.to_str()) { match hex::decode(name) { Ok(ref hex) => { - if hex[0..4] != key_type { continue } + if &hex[0..4] != &key_type.0 { continue } let public = TPublic::from_slice(&hex[4..]); public_keys.push(public); } @@ -151,48 +239,165 @@ impl Store { Ok(public_keys) } - fn key_file_path(&self, public: &TPair::Public) -> PathBuf { + /// Get public keys of all stored keys that match the key type. + /// + /// This will just use the type of the public key (a list of which to be returned) in order + /// to determine the key type. Unless you use a specialised application-type public key, then + /// this only give you keys registered under generic cryptography, and will not return keys + /// registered under the application type. + pub fn public_keys(&self) -> Result> { + self.public_keys_by_type::(Public::ID) + .map(|v| v.into_iter().map(Into::into).collect()) + } + + /// Returns the file path for the given public key and key type. + fn key_file_path(&self, public: &[u8], key_type: KeyTypeId) -> PathBuf { let mut buf = self.path.clone(); - let bytes: [u8; 4] = TPair::KEY_TYPE.to_le_bytes(); - let key_type = hex::encode(bytes); - let key = hex::encode(public.as_slice()); + let key_type = hex::encode(key_type.0); + let key = hex::encode(public); buf.push(key_type + key.as_str()); buf } } +impl BareCryptoStore for Store { + fn sr25519_public_keys(&self, key_type: KeyTypeId) -> Vec { + self.public_keys_by_type::(key_type).unwrap_or_default() + } + + fn sr25519_generate_new( + &mut self, + id: KeyTypeId, + seed: Option<&str>, + ) -> std::result::Result { + let pair = match seed { + Some(seed) => self.insert_ephemeral_from_seed_by_type::(seed, id), + None => self.generate_by_type::(id), + }.map_err(|e| e.to_string())?; + + Ok(pair.public()) + } + + fn sr25519_key_pair(&self, id: KeyTypeId, pub_key: &sr25519::Public) -> Option { + self.key_pair_by_type::(pub_key, id).ok() + } + + fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec { + self.public_keys_by_type::(key_type).unwrap_or_default() + } + + fn ed25519_generate_new( + &mut self, + id: KeyTypeId, + seed: Option<&str>, + ) -> std::result::Result { + let pair = match seed { + Some(seed) => self.insert_ephemeral_from_seed_by_type::(seed, id), + None => self.generate_by_type::(id), + }.map_err(|e| e.to_string())?; + + Ok(pair.public()) + } + + fn ed25519_key_pair(&self, id: KeyTypeId, pub_key: &ed25519::Public) -> Option { + self.key_pair_by_type::(pub_key, id).ok() + } + + fn insert_unknown(&mut self, key_type: KeyTypeId, suri: &str, public: &[u8]) + -> std::result::Result<(), ()> + { + Store::insert_unknown(self, key_type, suri, public).map_err(|_| ()) + } + + fn password(&self) -> Option<&str> { + self.password.as_ref().map(|x| x.as_str()) + } +} + #[cfg(test)] mod tests { use super::*; use tempdir::TempDir; - use primitives::ed25519; use primitives::crypto::Ss58Codec; #[test] fn basic_store() { let temp_dir = TempDir::new("keystore").unwrap(); - let store = Store::open(temp_dir.path().to_owned()).unwrap(); + let store = Store::open(temp_dir.path(), None).unwrap(); - assert!(store.contents::().unwrap().is_empty()); + assert!(store.read().public_keys::().unwrap().is_empty()); - let key: ed25519::Pair = store.generate("thepassword").unwrap(); - let key2: ed25519::Pair = store.load(&key.public(), "thepassword").unwrap(); - - assert!(store.load::(&key.public(), "notthepassword").is_err()); + let key: ed25519::AppPair = store.write().generate().unwrap(); + let key2: ed25519::AppPair = store.read().key_pair(&key.public()).unwrap(); assert_eq!(key.public(), key2.public()); - assert_eq!(store.contents::().unwrap()[0], key.public()); + assert_eq!(store.read().public_keys::().unwrap()[0], key.public()); } #[test] - fn test_generate_from_seed() { + fn test_insert_ephemeral_from_seed() { let temp_dir = TempDir::new("keystore").unwrap(); - let mut store = Store::open(temp_dir.path().to_owned()).unwrap(); + let store = Store::open(temp_dir.path(), None).unwrap(); - let pair: ed25519::Pair = store - .generate_from_seed("0x3d97c819d68f9bafa7d6e79cb991eebcd77d966c5334c0b94d9e1fa7ad0869dc") + let pair: ed25519::AppPair = store + .write() + .insert_ephemeral_from_seed("0x3d97c819d68f9bafa7d6e79cb991eebcd77d966c5334c0b94d9e1fa7ad0869dc") .unwrap(); - assert_eq!("5DKUrgFqCPV8iAXx9sjy1nyBygQCeiUYRFWurZGhnrn3HJCA", pair.public().to_ss58check()); + assert_eq!( + "5DKUrgFqCPV8iAXx9sjy1nyBygQCeiUYRFWurZGhnrn3HJCA", + pair.public().to_ss58check() + ); + + drop(store); + let store = Store::open(temp_dir.path(), None).unwrap(); + // Keys generated from seed should not be persisted! + assert!(store.read().key_pair::(&pair.public()).is_err()); + } + + #[test] + fn password_being_used() { + let password = String::from("password"); + let temp_dir = TempDir::new("keystore").unwrap(); + let store = Store::open(temp_dir.path(), Some(password.clone().into())).unwrap(); + + let pair: ed25519::AppPair = store.write().generate().unwrap(); + assert_eq!( + pair.public(), + store.read().key_pair::(&pair.public()).unwrap().public(), + ); + + // Without the password the key should not be retrievable + let store = Store::open(temp_dir.path(), None).unwrap(); + assert!(store.read().key_pair::(&pair.public()).is_err()); + + let store = Store::open(temp_dir.path(), Some(password.into())).unwrap(); + assert_eq!( + pair.public(), + store.read().key_pair::(&pair.public()).unwrap().public(), + ); + } + + #[test] + fn public_keys_are_returned() { + let temp_dir = TempDir::new("keystore").unwrap(); + let store = Store::open(temp_dir.path(), None).unwrap(); + + let mut public_keys = Vec::new(); + for i in 0..10 { + public_keys.push(store.write().generate::().unwrap().public()); + public_keys.push(store.write().insert_ephemeral_from_seed::( + &format!("0x3d97c819d68f9bafa7d6e79cb991eebcd7{}d966c5334c0b94d9e1fa7ad0869dc", i), + ).unwrap().public()); + } + + // Generate a key of a different type + store.write().generate::().unwrap(); + + public_keys.sort(); + let mut store_pubs = store.read().public_keys::().unwrap(); + store_pubs.sort(); + + assert_eq!(public_keys, store_pubs); } } diff --git a/core/network/Cargo.toml b/core/network/Cargo.toml index 383fc29fc670b..0600a60999c64 100644 --- a/core/network/Cargo.toml +++ b/core/network/Cargo.toml @@ -11,7 +11,7 @@ bytes = "0.4" derive_more = "0.14.0" either = "1.5.2" log = "0.4" -parking_lot = "0.8.0" +parking_lot = "0.9.0" bitflags = "1.0" fnv = "1.0" futures = "0.1.17" @@ -44,6 +44,7 @@ test-client = { package = "substrate-test-runtime-client", path = "../../core/te erased-serde = "0.3.9" void = "1.0" zeroize = "0.9.0" +babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../consensus/babe/primitives" } [dev-dependencies] env_logger = { version = "0.6" } diff --git a/core/network/src/config.rs b/core/network/src/config.rs index c05c127d835cb..6cabb1217b003 100644 --- a/core/network/src/config.rs +++ b/core/network/src/config.rs @@ -102,6 +102,11 @@ impl Roles { self.intersects(Roles::FULL | Roles::AUTHORITY) } + /// Does this role represents a client that does not participates in the consensus? + pub fn is_authority(&self) -> bool { + *self == Roles::AUTHORITY + } + /// Does this role represents a client that does not hold full chain data locally? pub fn is_light(&self) -> bool { !self.is_full() diff --git a/core/network/src/test/mod.rs b/core/network/src/test/mod.rs index c416d347d6b9c..110201057c783 100644 --- a/core/network/src/test/mod.rs +++ b/core/network/src/test/mod.rs @@ -57,7 +57,7 @@ use test_client::{self, AccountKeyring}; pub use test_client::runtime::{Block, Extrinsic, Hash, Transfer}; pub use test_client::TestClient; -type AuthorityId = primitives::sr25519::Public; +type AuthorityId = babe_primitives::AuthorityId; #[cfg(any(test, feature = "test-helpers"))] /// A Verifier that accepts all blocks and passes them on with the configured diff --git a/core/offchain/Cargo.toml b/core/offchain/Cargo.toml index 97a6fd3d2dc50..ed201c9316ad5 100644 --- a/core/offchain/Cargo.toml +++ b/core/offchain/Cargo.toml @@ -12,11 +12,12 @@ futures = "0.1.25" log = "0.4" offchain-primitives = { package = "substrate-offchain-primitives", path = "./primitives" } codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } -parking_lot = "0.8.0" +parking_lot = "0.9.0" primitives = { package = "substrate-primitives", path = "../../core/primitives" } sr-primitives = { path = "../../core/sr-primitives" } transaction_pool = { package = "substrate-transaction-pool", path = "../../core/transaction-pool" } network = { package = "substrate-network", path = "../../core/network" } +keystore = { package = "substrate-keystore", path = "../keystore" } [dev-dependencies] env_logger = "0.6" diff --git a/core/offchain/src/api.rs b/core/offchain/src/api.rs index 8733f3699a18a..c13b79131bcb3 100644 --- a/core/offchain/src/api.rs +++ b/core/offchain/src/api.rs @@ -21,174 +21,32 @@ use std::{ time::{SystemTime, Duration}, thread::sleep, }; + use client::backend::OffchainStorage; -use crate::AuthorityKeyProvider; use futures::{Stream, Future, sync::mpsc}; use log::{info, debug, warn, error}; +use network::{PeerId, Multiaddr, NetworkStateInfo}; use codec::{Encode, Decode}; use primitives::offchain::{ - Timestamp, - HttpRequestId, HttpRequestStatus, HttpError, - Externalities as OffchainExt, - CryptoKind, CryptoKey, - StorageKind, - OpaqueNetworkState, OpaquePeerId, OpaqueMultiaddr, -}; -use primitives::crypto::{Pair, Public, Protected}; -use primitives::{ed25519, sr25519}; -use sr_primitives::{ - generic::BlockId, - traits::{self, Extrinsic}, + Externalities as OffchainExt, HttpRequestId, Timestamp, HttpRequestStatus, HttpError, + OpaqueNetworkState, OpaquePeerId, OpaqueMultiaddr, StorageKind, }; +use sr_primitives::{generic::BlockId, traits::{self, Extrinsic}}; use transaction_pool::txpool::{Pool, ChainApi}; -use network::NetworkStateInfo; -use network::{PeerId, Multiaddr}; /// A message between the offchain extension and the processing thread. enum ExtMessage { SubmitExtrinsic(Vec), } -/// A persisted key seed. -#[derive(Encode, Decode)] -struct StoredKey { - kind: CryptoKind, - phrase: String, -} - -impl StoredKey { - fn generate_with_phrase(kind: CryptoKind, password: Option<&str>) -> Self { - match kind { - CryptoKind::Ed25519 => { - let phrase = ed25519::Pair::generate_with_phrase(password).1; - Self { kind, phrase } - } - CryptoKind::Sr25519 => { - let phrase = sr25519::Pair::generate_with_phrase(password).1; - Self { kind, phrase } - } - } - } - - fn to_local_key(&self, password: Option<&str>) -> Result { - match self.kind { - CryptoKind::Ed25519 => { - ed25519::Pair::from_phrase(&self.phrase, password) - .map(|x| LocalKey::Ed25519(x.0)) - } - CryptoKind::Sr25519 => { - sr25519::Pair::from_phrase(&self.phrase, password) - .map(|x| LocalKey::Sr25519(x.0)) - } - } - .map_err(|e| { - warn!("Error recovering Offchain Worker key. Password invalid? {:?}", e); - () - }) - } -} - -enum LocalKey { - Ed25519(ed25519::Pair), - Sr25519(sr25519::Pair), -} - -impl LocalKey { - fn public(&self) -> Result, ()> { - match self { - LocalKey::Ed25519(pair) => Ok(pair.public().to_raw_vec()), - LocalKey::Sr25519(pair) => Ok(pair.public().to_raw_vec()), - } - } - - fn sign(&self, data: &[u8]) -> Result, ()> { - match self { - LocalKey::Ed25519(pair) => { - let sig = pair.sign(data); - let bytes: &[u8] = sig.as_ref(); - Ok(bytes.to_vec()) - } - LocalKey::Sr25519(pair) => { - let sig = pair.sign(data); - let bytes: &[u8] = sig.as_ref(); - Ok(bytes.to_vec()) - } - } - } - - fn verify(&self, msg: &[u8], signature: &[u8]) -> Result { - match self { - LocalKey::Ed25519(pair) => { - Ok(ed25519::Pair::verify_weak(signature, msg, pair.public())) - } - LocalKey::Sr25519(pair) => { - Ok(sr25519::Pair::verify_weak(signature, msg, pair.public())) - } - } - } -} - -/// A key. -enum Key { - LocalKey(LocalKey), - AuthorityKey(ConsensusPair), - FgAuthorityKey(FinalityPair), -} - -impl Key { - fn public(&self) -> Result, ()> { - match self { - Key::LocalKey(local) => { - local.public() - } - Key::AuthorityKey(pair) => { - Ok(pair.public().to_raw_vec()) - } - Key::FgAuthorityKey(pair) => { - Ok(pair.public().to_raw_vec()) - } - } - } - - fn sign(&self, data: &[u8]) -> Result, ()> { - match self { - Key::LocalKey(local) => { - local.sign(data) - } - Key::AuthorityKey(pair) => { - Ok(pair.sign(data).as_ref().to_vec()) - } - Key::FgAuthorityKey(pair) => { - Ok(pair.sign(data).as_ref().to_vec()) - } - } - } - - fn verify(&self, msg: &[u8], signature: &[u8]) -> Result { - match self { - Key::LocalKey(local) => { - local.verify(msg, signature) - } - Key::AuthorityKey(pair) => { - Ok(ConsensusPair::verify_weak(signature, msg, pair.public())) - } - Key::FgAuthorityKey(pair) => { - Ok(FinalityPair::verify_weak(signature, msg, pair.public())) - } - } - } -} - /// Asynchronous offchain API. /// /// NOTE this is done to prevent recursive calls into the runtime (which are not supported currently). -pub(crate) struct Api { +pub(crate) struct Api { sender: mpsc::UnboundedSender, db: Storage, - keys_password: Protected, - key_provider: KeyProvider, network_state: Arc, - at: BlockId, + _at: BlockId, } fn unavailable_yet(name: &str) -> R { @@ -199,59 +57,10 @@ fn unavailable_yet(name: &str) -> R { const LOCAL_DB: &str = "LOCAL (fork-aware) DB"; const STORAGE_PREFIX: &[u8] = b"storage"; -const KEYS_PREFIX: &[u8] = b"keys"; - -const NEXT_ID: &[u8] = b"crypto_key_id"; - -impl Api where - Storage: OffchainStorage, - KeyProvider: AuthorityKeyProvider, - Block: traits::Block, -{ - fn password(&self) -> Option<&str> { - Some(self.keys_password.as_ref().as_str()) - } - - fn read_key( - &self, - key: CryptoKey, - ) -> Result, ()> { - match key { - CryptoKey::LocalKey { id, kind } => { - let key = self.db.get(KEYS_PREFIX, &id.encode()) - .and_then(|key| StoredKey::decode(&mut &*key).ok()) - .ok_or(())?; - if key.kind != kind { - warn!( - "Invalid crypto kind (got: {:?}, expected: {:?}), when requesting key {:?}", - key.kind, - kind, - id - ); - return Err(()) - } - Ok(Key::LocalKey(key.to_local_key(self.password())?)) - } - CryptoKey::AuthorityKey => { - let key = self.key_provider - .authority_key(&self.at) - .ok_or(())?; - Ok(Key::AuthorityKey(key)) - } - CryptoKey::FgAuthorityKey => { - let key = self.key_provider - .fg_authority_key(&self.at) - .ok_or(())?; - Ok(Key::FgAuthorityKey(key)) - } - } - } -} -impl OffchainExt for Api +impl OffchainExt for Api where Storage: OffchainStorage, - KeyProvider: AuthorityKeyProvider, Block: traits::Block, { fn submit_transaction(&mut self, ext: Vec) -> Result<(), ()> { @@ -261,30 +70,6 @@ where .map_err(|_| ()) } - fn new_crypto_key(&mut self, kind: CryptoKind) -> Result { - let key = StoredKey::generate_with_phrase(kind, self.password()); - let (id, id_encoded) = loop { - let encoded = self.db.get(KEYS_PREFIX, NEXT_ID); - let encoded_slice = encoded.as_ref().map(|x| x.as_slice()); - let new_id = encoded_slice.and_then(|mut x| u16::decode(&mut x).ok()).unwrap_or_default() - .checked_add(1) - .ok_or(())?; - let new_id_encoded = new_id.encode(); - - if self.db.compare_and_set(KEYS_PREFIX, NEXT_ID, encoded_slice, &new_id_encoded) { - break (new_id, new_id_encoded); - } - }; - - self.db.set(KEYS_PREFIX, &id_encoded, &key.encode()); - - Ok(CryptoKey::LocalKey { id, kind }) - } - - fn pubkey(&self, key: CryptoKey) -> Result, ()> { - self.read_key(key)?.public() - } - fn network_state(&self) -> Result { let external_addresses = self.network_state.external_addresses(); @@ -295,25 +80,6 @@ where Ok(OpaqueNetworkState::from(state)) } - fn encrypt(&mut self, _key: CryptoKey, _data: &[u8]) -> Result, ()> { - unavailable_yet::<()>("encrypt"); - Err(()) - } - - fn decrypt(&mut self, _key: CryptoKey, _data: &[u8]) -> Result, ()> { - unavailable_yet::<()>("decrypt"); - Err(()) - - } - - fn sign(&mut self, key: CryptoKey, data: &[u8]) -> Result, ()> { - self.read_key(key)?.sign(data) - } - - fn verify(&mut self, key: CryptoKey, msg: &[u8], signature: &[u8]) -> Result { - self.read_key(key)?.verify(msg, signature) - } - fn timestamp(&mut self) -> Timestamp { let now = SystemTime::now(); let epoch_duration = now.duration_since(SystemTime::UNIX_EPOCH); @@ -506,23 +272,19 @@ pub(crate) struct AsyncApi { impl AsyncApi { /// Creates new Offchain extensions API implementation an the asynchronous processing part. - pub fn new>( + pub fn new( transaction_pool: Arc>, db: S, - keys_password: Protected, - key_provider: P, at: BlockId, network_state: Arc, - ) -> (Api, AsyncApi) { + ) -> (Api, AsyncApi) { let (sender, rx) = mpsc::unbounded(); let api = Api { sender, db, - keys_password, - key_provider, network_state, - at, + _at: at, }; let async_api = AsyncApi { @@ -571,7 +333,6 @@ mod tests { use std::convert::TryFrom; use sr_primitives::traits::Zero; use client_db::offchain::LocalStorage; - use crate::tests::TestProvider; use network::PeerId; use test_client::runtime::Block; @@ -587,7 +348,7 @@ mod tests { } } - fn offchain_api() -> (Api, Block>, AsyncApi) { + fn offchain_api() -> (Api, AsyncApi) { let _ = env_logger::try_init(); let db = LocalStorage::new_test(); let client = Arc::new(test_client::new()); @@ -596,7 +357,12 @@ mod tests { ); let mock = Arc::new(MockNetworkStateInfo()); - AsyncApi::new(pool, db, "pass".to_owned().into(), TestProvider::default(), BlockId::Number(Zero::zero()), mock) + AsyncApi::new( + pool, + db, + BlockId::Number(Zero::zero()), + mock, + ) } #[test] @@ -680,49 +446,6 @@ mod tests { assert_eq!(api.local_storage_get(kind, key), Some(b"value".to_vec())); } - #[test] - fn should_create_a_new_key_and_sign_and_verify_stuff() { - let test = |kind: CryptoKind| { - // given - let mut api = offchain_api().0; - let msg = b"Hello world!"; - - // when - let key = api.new_crypto_key(kind).unwrap(); - let signature = api.sign(key, msg).unwrap(); - - // then - let res = api.verify(key, msg, &signature).unwrap(); - assert_eq!(res, true); - let res = api.verify(key, msg, &[]).unwrap(); - assert_eq!(res, false); - let res = api.verify(key, b"Different msg", &signature).unwrap(); - assert_eq!(res, false); - }; - - test(CryptoKind::Ed25519); - test(CryptoKind::Sr25519); - } - - #[test] - fn should_sign_and_verify_with_authority_key() { - // given - let mut api = offchain_api().0; - api.key_provider.ed_key = Some(ed25519::Pair::generate().0); - let msg = b"Hello world!"; - - // when - let signature = api.sign(CryptoKey::AuthorityKey, msg).unwrap(); - - // then - let res = api.verify(CryptoKey::AuthorityKey, msg, &signature).unwrap(); - assert_eq!(res, true); - let res = api.verify(CryptoKey::AuthorityKey, msg, &[]).unwrap(); - assert_eq!(res, false); - let res = api.verify(CryptoKey::AuthorityKey, b"Different msg", &signature).unwrap(); - assert_eq!(res, false); - } - #[test] fn should_convert_network_states() { // given diff --git a/core/offchain/src/lib.rs b/core/offchain/src/lib.rs index feacb535aac6a..5525546f2558a 100644 --- a/core/offchain/src/lib.rs +++ b/core/offchain/src/lib.rs @@ -40,18 +40,12 @@ use std::{ }; use client::runtime_api::ApiExt; -use log::{debug, warn}; -use primitives::{ - ExecutionContext, - crypto, -}; -use sr_primitives::{ - generic::BlockId, - traits::{self, ProvideRuntimeApi}, -}; use futures::future::Future; -use transaction_pool::txpool::{Pool, ChainApi}; +use log::{debug, warn}; use network::NetworkStateInfo; +use primitives::ExecutionContext; +use sr_primitives::{generic::BlockId, traits::{self, ProvideRuntimeApi}}; +use transaction_pool::txpool::{Pool, ChainApi}; mod api; @@ -59,61 +53,27 @@ pub mod testing; pub use offchain_primitives::OffchainWorkerApi; -/// Provides currently configured authority key. -pub trait AuthorityKeyProvider: Clone + 'static { - /// The crypto used by the block authoring algorithm. - type ConsensusPair: crypto::Pair; - /// The crypto used by the finality gadget. - type FinalityPair: crypto::Pair; - - /// Returns currently configured authority key. - fn authority_key(&self, block_id: &BlockId) -> Option; - - /// Returns currently configured finality gadget authority key. - fn fg_authority_key(&self, block_id: &BlockId) -> Option; -} - /// An offchain workers manager. -pub struct OffchainWorkers< - Client, - Storage, - KeyProvider, - Block: traits::Block, -> { +pub struct OffchainWorkers { client: Arc, db: Storage, - authority_key: KeyProvider, - keys_password: crypto::Protected, _block: PhantomData, } -impl OffchainWorkers< - Client, - Storage, - KeyProvider, - Block, -> { +impl OffchainWorkers { /// Creates new `OffchainWorkers`. - pub fn new( - client: Arc, - db: Storage, - authority_key: KeyProvider, - keys_password: crypto::Protected, - ) -> Self { + pub fn new(client: Arc, db: Storage) -> Self { Self { client, db, - authority_key, - keys_password, _block: PhantomData, } } } -impl fmt::Debug for OffchainWorkers< +impl fmt::Debug for OffchainWorkers< Client, Storage, - KeyProvider, Block, > { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -121,16 +81,14 @@ impl fmt::Debug for Offchain } } -impl OffchainWorkers< +impl OffchainWorkers< Client, Storage, - KeyProvider, Block, > where Block: traits::Block, Client: ProvideRuntimeApi + Send + Sync + 'static, Client::Api: OffchainWorkerApi, - KeyProvider: AuthorityKeyProvider + Send, Storage: client::backend::OffchainStorage + 'static, { /// Start the offchain workers after given block. @@ -152,8 +110,6 @@ impl OffchainWorkers< let (api, runner) = api::AsyncApi::new( pool.clone(), self.db.clone(), - self.keys_password.clone(), - self.authority_key.clone(), at.clone(), network_state.clone(), ); @@ -167,7 +123,7 @@ impl OffchainWorkers< let run = runtime.offchain_worker_with_context( &at, ExecutionContext::OffchainWorker(api), - number + number, ); if let Err(e) = run { log::error!("Error running offchain workers at {:?}: {:?}", at, e); @@ -197,7 +153,6 @@ fn spawn_worker(f: impl FnOnce() -> () + Send + 'static) { mod tests { use super::*; use futures::Future; - use primitives::{ed25519, sr25519}; use network::{Multiaddr, PeerId}; struct MockNetworkStateInfo(); @@ -212,49 +167,19 @@ mod tests { } } - #[derive(Clone)] - pub(crate) struct TestProvider { - _marker: PhantomData, - pub(crate) sr_key: Option, - pub(crate) ed_key: Option, - } - - impl Default for TestProvider { - fn default() -> Self { - Self { - _marker: PhantomData, - sr_key: None, - ed_key: None, - } - } - } - - impl AuthorityKeyProvider for TestProvider { - type ConsensusPair = ed25519::Pair; - type FinalityPair = sr25519::Pair; - - fn authority_key(&self, _: &BlockId) -> Option { - self.ed_key.clone() - } - - fn fg_authority_key(&self, _: &BlockId) -> Option { - self.sr_key.clone() - } - } - #[test] fn should_call_into_runtime_and_produce_extrinsic() { // given let _ = env_logger::try_init(); let runtime = tokio::runtime::Runtime::new().unwrap(); let client = Arc::new(test_client::new()); - let pool = Arc::new(Pool::new(Default::default(), ::transaction_pool::ChainApi::new(client.clone()))); + let pool = Arc::new(Pool::new(Default::default(), transaction_pool::ChainApi::new(client.clone()))); let db = client_db::offchain::LocalStorage::new_test(); - let mock = Arc::new(MockNetworkStateInfo()); + let network_state = Arc::new(MockNetworkStateInfo()); // when - let offchain = OffchainWorkers::new(client, db, TestProvider::default(), "".to_owned().into()); - runtime.executor().spawn(offchain.on_block_imported(&0u64, &pool, mock.clone())); + let offchain = OffchainWorkers::new(client, db); + runtime.executor().spawn(offchain.on_block_imported(&0u64, &pool, network_state.clone())); // then runtime.shutdown_on_idle().wait().unwrap(); diff --git a/core/offchain/src/testing.rs b/core/offchain/src/testing.rs index f1c38007ea0e8..e01e4f5f59a0e 100644 --- a/core/offchain/src/testing.rs +++ b/core/offchain/src/testing.rs @@ -28,8 +28,6 @@ use primitives::offchain::{ HttpRequestId as RequestId, HttpRequestStatus as RequestStatus, Timestamp, - CryptoKind, - CryptoKey, StorageKind, OpaqueNetworkState, }; @@ -144,47 +142,6 @@ impl offchain::Externalities for TestOffchainExt { unimplemented!("not needed in tests so far") } - fn pubkey(&self, _key: CryptoKey) -> Result, ()> { - unimplemented!("not needed in tests so far") - } - - fn new_crypto_key(&mut self, _crypto: CryptoKind) -> Result { - unimplemented!("not needed in tests so far") - } - - fn encrypt( - &mut self, - _key: CryptoKey, - _data: &[u8], - ) -> Result, ()> { - unimplemented!("not needed in tests so far") - } - - fn decrypt( - &mut self, - _key: CryptoKey, - _data: &[u8], - ) -> Result, ()> { - unimplemented!("not needed in tests so far") - } - - fn sign( - &mut self, - _key: CryptoKey, - _data: &[u8], - ) -> Result, ()> { - unimplemented!("not needed in tests so far") - } - - fn verify( - &mut self, - _key: CryptoKey, - _msg: &[u8], - _signature: &[u8], - ) -> Result { - unimplemented!("not needed in tests so far") - } - fn timestamp(&mut self) -> Timestamp { unimplemented!("not needed in tests so far") } diff --git a/core/primitives/Cargo.toml b/core/primitives/Cargo.toml index 49d5a0de3080f..085b7f52c1dff 100644 --- a/core/primitives/Cargo.toml +++ b/core/primitives/Cargo.toml @@ -29,7 +29,7 @@ regex = { version = "1.1", optional = true } num-traits = { version = "0.2", default-features = false } zeroize = { version = "0.9.2", default-features = false } lazy_static = { version = "1.3", optional = true } -parking_lot = { version = "0.8", optional = true } +parking_lot = { version = "0.9.0", optional = true } [dev-dependencies] substrate-serializer = { path = "../serializer" } diff --git a/core/primitives/src/crypto.rs b/core/primitives/src/crypto.rs index b3469e09b89a2..56a8528369214 100644 --- a/core/primitives/src/crypto.rs +++ b/core/primitives/src/crypto.rs @@ -18,13 +18,11 @@ //! Cryptographic utilities. // end::description[] -#[cfg(feature = "std")] -use std::convert::{TryFrom, TryInto}; +use rstd::convert::{TryFrom, TryInto}; #[cfg(feature = "std")] use parking_lot::Mutex; #[cfg(feature = "std")] use rand::{RngCore, rngs::OsRng}; -#[cfg(feature = "std")] use codec::{Encode, Decode}; #[cfg(feature = "std")] use regex::Regex; @@ -33,6 +31,8 @@ use base58::{FromBase58, ToBase58}; #[cfg(feature = "std")] use std::hash::Hash; use zeroize::Zeroize; +#[doc(hidden)] +pub use rstd::ops::Deref; /// The root phrase for our publicly known keys. pub const DEV_PHRASE: &str = "bottom drive obey lake curtain smoke basket hold race lonely fit walk"; @@ -83,6 +83,14 @@ impl AsRef for Protected { } } +impl rstd::ops::Deref for Protected { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + #[cfg(feature = "std")] impl std::fmt::Debug for Protected { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { @@ -279,12 +287,12 @@ pub trait Ss58Codec: Sized { } } -#[cfg(feature = "std")] /// Derivable key trait. pub trait Derive: Sized { /// Derive a child key from a series of given junctions. /// /// Will be `None` for public keys if there are any hard junctions in there. + #[cfg(feature = "std")] fn derive>(&self, _path: Iter) -> Option { None } @@ -457,8 +465,8 @@ impl + AsRef<[u8]> + Default + Derive> Ss58Codec for T { } /// Trait suitable for typical cryptographic PKI key public type. -pub trait Public: AsRef<[u8]> + TypedKey + PartialEq + Eq + Clone + Send + Sync { - /// A new instance from the given slice that should be 32 bytes long. +pub trait Public: AsRef<[u8]> + AsMut<[u8]> + Default + Derive + CryptoType + PartialEq + Eq + Clone + Send + Sync { + /// A new instance from the given slice. /// /// NOTE: No checking goes on to ensure this is a real public key. Only use it if /// you are certain that the array actually is a pubkey. GIGO! @@ -466,17 +474,85 @@ pub trait Public: AsRef<[u8]> + TypedKey + PartialEq + Eq + Clone + Send + Sync /// Return a `Vec` filled with raw data. #[cfg(feature = "std")] - fn to_raw_vec(&self) -> Vec; + fn to_raw_vec(&self) -> Vec { self.as_slice().to_owned() } /// Return a slice filled with raw data. - fn as_slice(&self) -> &[u8]; + fn as_slice(&self) -> &[u8] { self.as_ref() } +} + +#[cfg(feature = "std")] +pub use self::dummy::*; + +#[cfg(feature = "std")] +mod dummy { + use super::*; + + /// Dummy cryptography. Doesn't do anything. + #[derive(Clone, Hash, Default, Eq, PartialEq)] + pub struct Dummy; + + impl AsRef<[u8]> for Dummy { + fn as_ref(&self) -> &[u8] { &b""[..] } + } + + impl AsMut<[u8]> for Dummy { + fn as_mut(&mut self) -> &mut[u8] { + unsafe { + #[allow(mutable_transmutes)] + rstd::mem::transmute::<_, &'static mut [u8]>(&b""[..]) + } + } + } + + impl CryptoType for Dummy { + type Pair = Dummy; + } + + impl Derive for Dummy {} + + impl Public for Dummy { + fn from_slice(_: &[u8]) -> Self { Self } + #[cfg(feature = "std")] + fn to_raw_vec(&self) -> Vec { vec![] } + fn as_slice(&self) -> &[u8] { b"" } + } + + impl Pair for Dummy { + type Public = Dummy; + type Seed = Dummy; + type Signature = Dummy; + type DeriveError = (); + fn generate_with_phrase(_: Option<&str>) -> (Self, String, Self::Seed) { Default::default() } + fn from_phrase(_: &str, _: Option<&str>) + -> Result<(Self, Self::Seed), SecretStringError> + { + Ok(Default::default()) + } + fn derive< + Iter: Iterator + >(&self, _: Iter) -> Result { Ok(Self) } + fn from_seed(_: &Self::Seed) -> Self { Self } + fn from_seed_slice(_: &[u8]) -> Result { Ok(Self) } + fn from_standard_components< + I: Iterator + >( + _: &str, + _: Option<&str>, + _: I + ) -> Result { Ok(Self) } + fn sign(&self, _: &[u8]) -> Self::Signature { Self } + fn verify>(_: &Self::Signature, _: M, _: &Self::Public) -> bool { true } + fn verify_weak, M: AsRef<[u8]>>(_: &[u8], _: M, _: P) -> bool { true } + fn public(&self) -> Self::Public { Self } + fn to_raw_vec(&self) -> Vec { vec![] } + } } /// Trait suitable for typical cryptographic PKI key pair type. /// /// For now it just specifies how to create a key from a phrase and derivation path. #[cfg(feature = "std")] -pub trait Pair: TypedKey + Sized + Clone + Send + Sync + 'static { +pub trait Pair: CryptoType + Sized + Clone + Send + Sync + 'static { /// The type which is used to encode a public key. type Public: Public + Hash; @@ -538,7 +614,7 @@ pub trait Pair: TypedKey + Sized + Clone + Send + Sync + 'static { fn sign(&self, message: &[u8]) -> Self::Signature; /// Verify a signature on a message. Returns true if the signature is good. - fn verify, M: AsRef<[u8]>>(sig: &Self::Signature, message: M, pubkey: P) -> bool; + fn verify>(sig: &Self::Signature, message: M, pubkey: &Self::Public) -> bool; /// Verify a signature on a message. Returns true if the signature is good. fn verify_weak, M: AsRef<[u8]>>(sig: &[u8], message: M, pubkey: P) -> bool; @@ -603,26 +679,109 @@ pub trait Pair: TypedKey + Sized + Clone + Send + Sync + 'static { fn to_raw_vec(&self) -> Vec; } +/// One type is wrapped by another. +pub trait IsWrappedBy: From + Into { + /// Get a reference to the inner from the outer. + fn from_ref(outer: &Outer) -> &Self; + /// Get a mutable reference to the inner from the outer. + fn from_mut(outer: &mut Outer) -> &mut Self; +} + +/// Opposite of `IsWrappedBy` - denotes a type which is a simple wrapper around another type. +pub trait Wraps: Sized { + /// The inner type it is wrapping. + type Inner: IsWrappedBy; +} + +impl IsWrappedBy for T where + Outer: AsRef + AsMut + From, + T: From, +{ + /// Get a reference to the inner from the outer. + fn from_ref(outer: &Outer) -> &Self { outer.as_ref() } + + /// Get a mutable reference to the inner from the outer. + fn from_mut(outer: &mut Outer) -> &mut Self { outer.as_mut() } +} + +impl UncheckedFrom for Outer where + Outer: Wraps, + Inner: IsWrappedBy + UncheckedFrom, +{ + fn unchecked_from(t: T) -> Self { + let inner: Inner = t.unchecked_into(); + inner.into() + } +} + +/// Type which has a particular kind of crypto associated with it. +pub trait CryptoType { + /// The pair key type of this crypto. + #[cfg(feature="std")] + type Pair: Pair; +} + /// An identifier for a type of cryptographic key. /// -/// 0-1024 are reserved. -pub type KeyTypeId = u32; +/// To avoid clashes with other modules when distributing your module publically, register your +/// `KeyTypeId` on the list here by making a PR. +/// +/// Values whose first character is `_` are reserved for private use and won't conflict with any +/// public modules. +#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct KeyTypeId(pub [u8; 4]); -/// Constant key types. -pub mod key_types { - use super::KeyTypeId; +impl From for KeyTypeId { + fn from(x: u32) -> Self { + Self(x.to_le_bytes()) + } +} - /// ED25519 public key. - pub const ED25519: KeyTypeId = 10; +impl From for u32 { + fn from(x: KeyTypeId) -> Self { + u32::from_le_bytes(x.0) + } +} - /// SR25519 public key. - pub const SR25519: KeyTypeId = 20; +impl<'a> TryFrom<&'a str> for KeyTypeId { + type Error = (); + fn try_from(x: &'a str) -> Result { + let b = x.as_bytes(); + if b.len() != 4 { + return Err(()); + } + let mut res = KeyTypeId::default(); + res.0.copy_from_slice(&b[0..4]); + Ok(res) + } } -/// A trait for something that has a key type ID. -pub trait TypedKey { - /// The type ID of this key. - const KEY_TYPE: KeyTypeId; +/// Known key types; this also functions as a global registry of key types for projects wishing to +/// avoid collisions with each other. +/// +/// It's not universal in the sense that *all* key types need to be mentioned here, it's just a +/// handy place to put common key types. +pub mod key_types { + use super::KeyTypeId; + + /// Key type for generic S/R 25519 key. + pub const SR25519: KeyTypeId = KeyTypeId(*b"sr25"); + /// Key type for generic Ed25519 key. + pub const ED25519: KeyTypeId = KeyTypeId(*b"ed25"); + /// Key type for Babe module, build-in. + pub const BABE: KeyTypeId = KeyTypeId(*b"babe"); + /// Key type for Grandpa module, build-in. + pub const GRANDPA: KeyTypeId = KeyTypeId(*b"gran"); + /// Key type for controlling an account in a Substrate runtime, built-in. + pub const ACCOUNT: KeyTypeId = KeyTypeId(*b"acco"); + /// Key type for Aura module, built-in. + pub const AURA: KeyTypeId = KeyTypeId(*b"aura"); + /// Key type for ImOnline module, built-in. + pub const IM_ONLINE: KeyTypeId = KeyTypeId(*b"imon"); + /// A key type ID useful for tests. + #[cfg(feature = "std")] + pub const DUMMY: KeyTypeId = KeyTypeId(*b"dumy"); } #[cfg(test)] @@ -639,14 +798,31 @@ mod tests { Standard{phrase: String, password: Option, path: Vec}, Seed(Vec), } + impl Default for TestPair { + fn default() -> Self { + TestPair::Generated + } + } + impl CryptoType for TestPair { + type Pair = Self; + } - #[derive(Clone, PartialEq, Eq, Hash)] + #[derive(Clone, PartialEq, Eq, Hash, Default)] struct TestPublic; impl AsRef<[u8]> for TestPublic { fn as_ref(&self) -> &[u8] { &[] } } + impl AsMut<[u8]> for TestPublic { + fn as_mut(&mut self) -> &mut [u8] { + &mut [] + } + } + impl CryptoType for TestPublic { + type Pair = TestPair; + } + impl Derive for TestPublic {} impl Public for TestPublic { fn from_slice(_bytes: &[u8]) -> Self { Self @@ -658,9 +834,6 @@ mod tests { vec![] } } - impl TypedKey for TestPublic { - const KEY_TYPE: u32 = 4242; - } impl Pair for TestPair { type Public = TestPublic; type Seed = [u8; 0]; @@ -686,11 +859,7 @@ mod tests { } fn from_seed(_seed: &::Seed) -> Self { TestPair::Seed(vec![]) } fn sign(&self, _message: &[u8]) -> Self::Signature { [] } - fn verify, M: AsRef<[u8]>>( - _sig: &Self::Signature, - _message: M, - _pubkey: P - ) -> bool { true } + fn verify>(_: &Self::Signature, _: M, _: &Self::Public) -> bool { true } fn verify_weak, M: AsRef<[u8]>>( _sig: &[u8], _message: M, @@ -717,9 +886,6 @@ mod tests { vec![] } } - impl TypedKey for TestPair { - const KEY_TYPE: u32 = 4242; - } #[test] fn interpret_std_seed_should_work() { diff --git a/core/primitives/src/ed25519.rs b/core/primitives/src/ed25519.rs index 0ff004291152c..810e86767d8e3 100644 --- a/core/primitives/src/ed25519.rs +++ b/core/primitives/src/ed25519.rs @@ -29,10 +29,10 @@ use substrate_bip39::seed_from_entropy; #[cfg(feature = "std")] use bip39::{Mnemonic, Language, MnemonicType}; #[cfg(feature = "std")] -use crate::crypto::{Pair as TraitPair, DeriveJunction, SecretStringError, Derive, Ss58Codec}; +use crate::crypto::{Pair as TraitPair, DeriveJunction, SecretStringError, Ss58Codec}; #[cfg(feature = "std")] use serde::{de, Serializer, Serialize, Deserializer, Deserialize}; -use crate::crypto::{key_types, KeyTypeId, Public as TraitPublic, TypedKey, UncheckedFrom}; +use crate::{crypto::{Public as TraitPublic, UncheckedFrom, CryptoType, Derive}}; /// A secret seed. It's not called a "secret key" because ring doesn't expose the secret keys /// of the key pair (yeah, dumb); as such we're forced to remember the seed manually if we @@ -77,6 +77,20 @@ impl AsMut<[u8]> for Public { } } +impl rstd::convert::TryFrom<&[u8]> for Public { + type Error = (); + + fn try_from(data: &[u8]) -> Result { + if data.len() == 32 { + let mut inner = [0u8; 32]; + inner.copy_from_slice(data); + Ok(Public(inner)) + } else { + Err(()) + } + } +} + impl From for [u8; 32] { fn from(x: Public) -> Self { x.0 @@ -90,12 +104,6 @@ impl From for Public { } } -impl AsRef for Public { - fn as_ref(&self) -> &Public { - &self - } -} - impl From for H256 { fn from(x: Public) -> Self { x.0.into() @@ -115,15 +123,15 @@ impl UncheckedFrom for Public { } #[cfg(feature = "std")] -impl ::std::fmt::Display for Public { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +impl std::fmt::Display for Public { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.to_ss58check()) } } #[cfg(feature = "std")] -impl ::std::fmt::Debug for Public { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +impl std::fmt::Debug for Public { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let s = self.to_ss58check(); write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.0), &s[0..8]) } @@ -145,8 +153,8 @@ impl<'de> Deserialize<'de> for Public { } #[cfg(feature = "std")] -impl ::std::hash::Hash for Public { - fn hash(&self, state: &mut H) { +impl std::hash::Hash for Public { + fn hash(&self, state: &mut H) { self.0.hash(state); } } @@ -155,6 +163,20 @@ impl ::std::hash::Hash for Public { #[derive(Encode, Decode)] pub struct Signature(pub [u8; 64]); +impl rstd::convert::TryFrom<&[u8]> for Signature { + type Error = (); + + fn try_from(data: &[u8]) -> Result { + if data.len() == 64 { + let mut inner = [0u8; 64]; + inner.copy_from_slice(data); + Ok(Signature(inner)) + } else { + Err(()) + } + } +} + impl Clone for Signature { fn clone(&self) -> Self { let mut r = [0u8; 64]; @@ -171,7 +193,7 @@ impl Default for Signature { impl PartialEq for Signature { fn eq(&self, b: &Self) -> bool { - &self.0[..] == &b.0[..] + self.0[..] == b.0[..] } } @@ -208,16 +230,16 @@ impl AsMut<[u8]> for Signature { } #[cfg(feature = "std")] -impl ::std::fmt::Debug for Signature { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +impl std::fmt::Debug for Signature { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", crate::hexdisplay::HexDisplay::from(&self.0)) } } #[cfg(feature = "std")] -impl ::std::hash::Hash for Signature { - fn hash(&self, state: &mut H) { - ::std::hash::Hash::hash(&self.0[..], state); +impl std::hash::Hash for Signature { + fn hash(&self, state: &mut H) { + std::hash::Hash::hash(&self.0[..], state); } } @@ -306,31 +328,10 @@ impl TraitPublic for Public { r.copy_from_slice(data); Public(r) } - - /// Return a `Vec` filled with raw data. - #[cfg(feature = "std")] - fn to_raw_vec(&self) -> Vec { - let r: &[u8; 32] = self.as_ref(); - r.to_vec() - } - - /// Return a slice filled with raw data. - fn as_slice(&self) -> &[u8] { - let r: &[u8; 32] = self.as_ref(); - &r[..] - } } -#[cfg(feature = "std")] impl Derive for Public {} -#[cfg(feature = "std")] -impl AsRef for Pair { - fn as_ref(&self) -> &Pair { - &self - } -} - /// Derive a single hard junction. #[cfg(feature = "std")] fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed { @@ -438,8 +439,8 @@ impl TraitPair for Pair { } /// Verify a signature on a message. Returns true if the signature is good. - fn verify, M: AsRef<[u8]>>(sig: &Self::Signature, message: M, pubkey: P) -> bool { - Self::verify_weak(&sig.0[..], message.as_ref(), &pubkey.as_ref().0[..]) + fn verify>(sig: &Self::Signature, message: M, pubkey: &Self::Public) -> bool { + Self::verify_weak(&sig.0[..], message.as_ref(), pubkey) } /// Verify a signature on a message. Returns true if the signature is good. @@ -488,17 +489,19 @@ impl Pair { } } -impl TypedKey for Public { - const KEY_TYPE: KeyTypeId = key_types::ED25519; +impl CryptoType for Public { + #[cfg(feature="std")] + type Pair = Pair; } -impl TypedKey for Signature { - const KEY_TYPE: KeyTypeId = key_types::ED25519; +impl CryptoType for Signature { + #[cfg(feature="std")] + type Pair = Pair; } #[cfg(feature = "std")] -impl TypedKey for Pair { - const KEY_TYPE: KeyTypeId = key_types::ED25519; +impl CryptoType for Pair { + type Pair = Pair; } #[cfg(test)] diff --git a/core/primitives/src/lib.rs b/core/primitives/src/lib.rs index bf1911dad93d5..21e7c878082ad 100644 --- a/core/primitives/src/lib.rs +++ b/core/primitives/src/lib.rs @@ -33,11 +33,13 @@ macro_rules! map { use rstd::prelude::*; use rstd::ops::Deref; -use codec::{Encode, Decode}; #[cfg(feature = "std")] use std::borrow::Cow; #[cfg(feature = "std")] use serde::{Serialize, Deserialize}; +#[cfg(feature = "std")] +pub use serde;// << for macro +pub use codec::{Encode, Decode};// << for macro #[cfg(feature = "std")] pub use impl_serde::serialize as bytes; @@ -61,6 +63,8 @@ pub mod sandbox; pub mod storage; pub mod uint; mod changes_trie; +pub mod traits; +pub mod testing; #[cfg(test)] mod tests; @@ -77,7 +81,6 @@ pub use hash_db::Hasher; pub use self::hasher::blake2::Blake2Hasher; /// Context for executing a call into the runtime. -#[repr(u8)] pub enum ExecutionContext { /// Context for general importing (including own blocks). Importing, @@ -91,6 +94,17 @@ pub enum ExecutionContext { Other, } +impl ExecutionContext { + /// Returns if the keystore should be enabled for the current context. + pub fn enable_keystore(&self) -> bool { + use ExecutionContext::*; + match self { + Importing | Syncing | BlockConstruction => false, + OffchainWorker(_) | Other => true, + } + } +} + /// Hex-serialized shim for `Vec`. #[derive(PartialEq, Eq, Clone)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord))] @@ -147,7 +161,7 @@ impl ::std::fmt::Debug for NativeOrEncoded { #[cfg(feature = "std")] impl NativeOrEncoded { /// Return the value as the encoded format. - pub fn as_encoded<'a>(&'a self) -> Cow<'a, [u8]> { + pub fn as_encoded(&self) -> Cow<'_, [u8]> { match self { NativeOrEncoded::Encoded(e) => Cow::Borrowed(e.as_slice()), NativeOrEncoded::Native(n) => Cow::Owned(n.encode()), @@ -199,3 +213,4 @@ impl codec::Decode for NeverNativeValue { Err("`NeverNativeValue` should never be decoded".into()) } } + diff --git a/core/primitives/src/offchain.rs b/core/primitives/src/offchain.rs index a0ee1c34d1a28..8ce5863e010ec 100644 --- a/core/primitives/src/offchain.rs +++ b/core/primitives/src/offchain.rs @@ -16,11 +16,12 @@ //! Offchain workers types -use crate::crypto; use codec::{Encode, Decode}; use rstd::prelude::{Vec, Box}; use rstd::convert::TryFrom; +pub use crate::crypto::KeyTypeId; + /// A type of supported crypto. #[derive(Clone, Copy, PartialEq, Eq, Encode, Decode)] #[cfg_attr(feature = "std", derive(Debug))] @@ -58,81 +59,6 @@ impl From for u32 { } } -/// A type of supported crypto. -#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] -#[repr(C)] -pub enum CryptoKind { - /// SR25519 crypto (Schnorrkel) - Sr25519 = crypto::key_types::SR25519 as isize, - /// ED25519 crypto (Edwards) - Ed25519 = crypto::key_types::ED25519 as isize, -} - -impl TryFrom for CryptoKind { - type Error = (); - - fn try_from(kind: u32) -> Result { - match kind { - e if e == CryptoKind::Sr25519 as isize as u32 => Ok(CryptoKind::Sr25519), - e if e == CryptoKind::Ed25519 as isize as u32 => Ok(CryptoKind::Ed25519), - _ => Err(()), - } - } -} - -impl From for u32 { - fn from(c: CryptoKind) -> Self { - c as isize as u32 - } -} - -/// Key to use in the offchain worker crypto api. -#[derive(Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] -pub enum CryptoKey { - /// Use a key from the offchain workers local storage. - LocalKey { - /// The id of the key. - id: u16, - /// The kind of the key. - kind: CryptoKind, - }, - /// Use the key the block authoring algorithm uses. - AuthorityKey, - /// Use the key the finality gadget uses. - FgAuthorityKey, -} - -impl TryFrom for CryptoKey { - type Error = (); - - fn try_from(key: u64) -> Result { - match key & 0xFF { - 0 => { - let id = (key >> 8 & 0xFFFF) as u16; - let kind = CryptoKind::try_from((key >> 32) as u32)?; - Ok(CryptoKey::LocalKey { id, kind }) - } - 1 => Ok(CryptoKey::AuthorityKey), - 2 => Ok(CryptoKey::FgAuthorityKey), - _ => Err(()), - } - } -} - -impl From for u64 { - fn from(key: CryptoKey) -> u64 { - match key { - CryptoKey::LocalKey { id, kind } => { - ((kind as u64) << 32) | ((id as u64) << 8) - } - CryptoKey::AuthorityKey => 1, - CryptoKey::FgAuthorityKey => 2, - } - } -} - /// Opaque type for offchain http requests. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "std", derive(Debug))] @@ -314,45 +240,6 @@ pub trait Externalities { /// Returns information about the local node's network state. fn network_state(&self) -> Result; - /// Create new key(pair) for signing/encryption/decryption. - /// - /// Returns an error if given crypto kind is not supported. - fn new_crypto_key(&mut self, crypto: CryptoKind) -> Result; - - /// Returns the locally configured authority public key, if available. - fn pubkey(&self, key: CryptoKey) -> Result, ()>; - - /// Encrypt a piece of data using given crypto key. - /// - /// If `key` is `None`, it will attempt to use current authority key of `CryptoKind`. - /// - /// Returns an error if `key` is not available or does not exist, - /// or the expected `CryptoKind` does not match. - fn encrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result, ()>; - - /// Decrypt a piece of data using given crypto key. - /// - /// If `key` is `None`, it will attempt to use current authority key of `CryptoKind`. - /// - /// Returns an error if data cannot be decrypted or the `key` is not available or does not exist, - /// or the expected `CryptoKind` does not match. - fn decrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result, ()>; - - /// Sign a piece of data using given crypto key. - /// - /// If `key` is `None`, it will attempt to use current authority key of `CryptoKind`. - /// - /// Returns an error if `key` is not available or does not exist, - /// or the expected `CryptoKind` does not match. - fn sign(&mut self, key: CryptoKey, data: &[u8]) -> Result, ()>; - - /// Verifies that `signature` for `msg` matches given `key`. - /// - /// Returns an `Ok` with `true` in case it does, `false` in case it doesn't. - /// Returns an error in case the key is not available or does not exist or the parameters - /// lengths are incorrect or `CryptoKind` does not match. - fn verify(&mut self, key: CryptoKey, msg: &[u8], signature: &[u8]) -> Result; - /// Returns current UNIX timestamp (in millis) fn timestamp(&mut self) -> Timestamp; @@ -466,34 +353,10 @@ impl Externalities for Box { (&mut **self).submit_transaction(ex) } - fn new_crypto_key(&mut self, crypto: CryptoKind) -> Result { - (&mut **self).new_crypto_key(crypto) - } - - fn encrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result, ()> { - (&mut **self).encrypt(key, data) - } - fn network_state(&self) -> Result { (& **self).network_state() } - fn pubkey(&self, key: CryptoKey) -> Result, ()> { - (&**self).pubkey(key) - } - - fn decrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result, ()> { - (&mut **self).decrypt(key, data) - } - - fn sign(&mut self, key: CryptoKey, data: &[u8]) -> Result, ()> { - (&mut **self).sign(key, data) - } - - fn verify(&mut self, key: CryptoKey, msg: &[u8], signature: &[u8]) -> Result { - (&mut **self).verify(key, msg, signature) - } - fn timestamp(&mut self) -> Timestamp { (&mut **self).timestamp() } @@ -571,27 +434,4 @@ mod tests { assert_eq!(t.sub(Duration::from_millis(10)), Timestamp(0)); assert_eq!(t.diff(&Timestamp(3)), Duration(2)); } - - #[test] - fn crypto_key_to_from_u64() { - let key = CryptoKey::AuthorityKey; - let uint: u64 = key.clone().into(); - let key2 = CryptoKey::try_from(uint).unwrap(); - assert_eq!(key, key2); - - let key = CryptoKey::FgAuthorityKey; - let uint: u64 = key.clone().into(); - let key2 = CryptoKey::try_from(uint).unwrap(); - assert_eq!(key, key2); - - let key = CryptoKey::LocalKey { id: 0, kind: CryptoKind::Ed25519 }; - let uint: u64 = key.clone().into(); - let key2 = CryptoKey::try_from(uint).unwrap(); - assert_eq!(key, key2); - - let key = CryptoKey::LocalKey { id: 10, kind: CryptoKind::Sr25519 }; - let uint: u64 = key.clone().into(); - let key2 = CryptoKey::try_from(uint).unwrap(); - assert_eq!(key, key2); - } } diff --git a/core/primitives/src/sr25519.rs b/core/primitives/src/sr25519.rs index b0c217c64fee7..0e573f49ce34c 100644 --- a/core/primitives/src/sr25519.rs +++ b/core/primitives/src/sr25519.rs @@ -30,8 +30,10 @@ use substrate_bip39::mini_secret_from_entropy; #[cfg(feature = "std")] use bip39::{Mnemonic, Language, MnemonicType}; #[cfg(feature = "std")] -use crate::crypto::{Pair as TraitPair, DeriveJunction, Infallible, SecretStringError, Derive, Ss58Codec}; -use crate::crypto::{key_types, KeyTypeId, Public as TraitPublic, TypedKey, UncheckedFrom}; +use crate::crypto::{ + Pair as TraitPair, DeriveJunction, Infallible, SecretStringError, Ss58Codec +}; +use crate::{crypto::{Public as TraitPublic, UncheckedFrom, CryptoType, Derive}}; use crate::hash::{H256, H512}; use codec::{Encode, Decode}; @@ -56,19 +58,13 @@ pub struct Pair(Keypair); impl Clone for Pair { fn clone(&self) -> Self { Pair(schnorrkel::Keypair { - public: self.0.public.clone(), + public: self.0.public, secret: schnorrkel::SecretKey::from_bytes(&self.0.secret.to_bytes()[..]) .expect("key is always the correct size; qed") }) } } -impl AsRef for Public { - fn as_ref(&self) -> &Public { - &self - } -} - impl AsRef<[u8; 32]> for Public { fn as_ref(&self) -> &[u8; 32] { &self.0 @@ -99,6 +95,20 @@ impl From for H256 { } } +impl rstd::convert::TryFrom<&[u8]> for Public { + type Error = (); + + fn try_from(data: &[u8]) -> Result { + if data.len() == 32 { + let mut inner = [0u8; 32]; + inner.copy_from_slice(data); + Ok(Public(inner)) + } else { + Err(()) + } + } +} + impl UncheckedFrom<[u8; 32]> for Public { fn unchecked_from(x: [u8; 32]) -> Self { Public::from_raw(x) @@ -112,15 +122,15 @@ impl UncheckedFrom for Public { } #[cfg(feature = "std")] -impl ::std::fmt::Display for Public { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +impl std::fmt::Display for Public { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.to_ss58check()) } } #[cfg(feature = "std")] -impl ::std::fmt::Debug for Public { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { +impl std::fmt::Debug for Public { + fn fmt(&self, f: &mut std::fmt::Formatter) -> ::std::fmt::Result { let s = self.to_ss58check(); write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.0), &s[0..8]) } @@ -142,8 +152,8 @@ impl<'de> Deserialize<'de> for Public { } #[cfg(feature = "std")] -impl ::std::hash::Hash for Public { - fn hash(&self, state: &mut H) { +impl std::hash::Hash for Public { + fn hash(&self, state: &mut H) { self.0.hash(state); } } @@ -154,6 +164,20 @@ impl ::std::hash::Hash for Public { #[derive(Encode, Decode)] pub struct Signature(pub [u8; 64]); +impl rstd::convert::TryFrom<&[u8]> for Signature { + type Error = (); + + fn try_from(data: &[u8]) -> Result { + if data.len() == 64 { + let mut inner = [0u8; 64]; + inner.copy_from_slice(data); + Ok(Signature(inner)) + } else { + Err(()) + } + } +} + impl Clone for Signature { fn clone(&self) -> Self { let mut r = [0u8; 64]; @@ -170,7 +194,7 @@ impl Default for Signature { impl PartialEq for Signature { fn eq(&self, b: &Self) -> bool { - &self.0[..] == &b.0[..] + self.0[..] == b.0[..] } } @@ -268,11 +292,11 @@ impl Signature { } } -#[cfg(feature = "std")] impl Derive for Public { /// Derive a child key from a series of given junctions. /// /// `None` if there are any hard junctions in there. + #[cfg(feature = "std")] fn derive>(&self, path: Iter) -> Option { let mut acc = PublicKey::from_bytes(self.as_ref()).ok()?; for j in path { @@ -318,24 +342,6 @@ impl TraitPublic for Public { r.copy_from_slice(data); Public(r) } - - /// Return a `Vec` filled with raw data. - #[cfg(feature = "std")] - fn to_raw_vec(&self) -> Vec { - self.0.to_vec() - } - - /// Return a slice filled with raw data. - fn as_slice(&self) -> &[u8] { - &self.0 - } -} - -#[cfg(feature = "std")] -impl AsRef for Pair { - fn as_ref(&self) -> &Pair { - &self - } } #[cfg(feature = "std")] @@ -475,20 +481,15 @@ impl TraitPair for Pair { } /// Verify a signature on a message. Returns true if the signature is good. - fn verify, M: AsRef<[u8]>>(sig: &Self::Signature, message: M, pubkey: P) -> bool { - // Match both schnorrkel 0.1.1 and 0.8.0+ signatures, supporting both wallets - // that have not been upgraded and those that have. To swap to 0.8.0 only, - // create `schnorrkel::Signature` and pass that into `verify_simple` - match PublicKey::from_bytes(pubkey.as_ref().as_slice()) { - Ok(pk) => pk.verify_simple_preaudit_deprecated( - SIGNING_CTX, message.as_ref(), &sig.as_ref(), - ).is_ok(), - Err(_) => false, - } + fn verify>(sig: &Self::Signature, message: M, pubkey: &Self::Public) -> bool { + Self::verify_weak(&sig.0[..], message, pubkey) } /// Verify a signature on a message. Returns true if the signature is good. fn verify_weak, M: AsRef<[u8]>>(sig: &[u8], message: M, pubkey: P) -> bool { + // Match both schnorrkel 0.1.1 and 0.8.0+ signatures, supporting both wallets + // that have not been upgraded and those that have. To swap to 0.8.0 only, + // create `schnorrkel::Signature` and pass that into `verify_simple` match PublicKey::from_bytes(pubkey.as_ref()) { Ok(pk) => pk.verify_simple_preaudit_deprecated( SIGNING_CTX, message.as_ref(), &sig, @@ -518,17 +519,19 @@ impl Pair { } } -impl TypedKey for Public { - const KEY_TYPE: KeyTypeId = key_types::SR25519; +impl CryptoType for Public { + #[cfg(feature="std")] + type Pair = Pair; } -impl TypedKey for Signature { - const KEY_TYPE: KeyTypeId = key_types::SR25519; +impl CryptoType for Signature { + #[cfg(feature="std")] + type Pair = Pair; } #[cfg(feature = "std")] -impl TypedKey for Pair { - const KEY_TYPE: KeyTypeId = key_types::SR25519; +impl CryptoType for Pair { + type Pair = Pair; } #[cfg(test)] @@ -713,6 +716,6 @@ mod test { let js_signature = Signature::from_raw(hex!( "28a854d54903e056f89581c691c1f7d2ff39f8f896c9e9c22475e60902cc2b3547199e0e91fa32902028f2ca2355e8cdd16cfe19ba5e8b658c94aa80f3b81a00" )); - assert!(Pair::verify(&js_signature, b"SUBSTRATE", public)); + assert!(Pair::verify(&js_signature, b"SUBSTRATE", &public)); } } diff --git a/core/primitives/src/testing.rs b/core/primitives/src/testing.rs new file mode 100644 index 0000000000000..6d91c83ccb6e3 --- /dev/null +++ b/core/primitives/src/testing.rs @@ -0,0 +1,115 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Types that should only be used for testing! + +#[cfg(feature = "std")] +use crate::{ed25519, sr25519, crypto::{Public, Pair, KeyTypeId}}; + +/// A keystore implementation usable in tests. +#[cfg(feature = "std")] +#[derive(Default)] +pub struct KeyStore { + /// `KeyTypeId` maps to public keys and public keys map to private keys. + keys: std::collections::HashMap, Vec>>, +} + +#[cfg(feature = "std")] +impl KeyStore { + /// Creates a new instance of `Self`. + pub fn new() -> std::sync::Arc> { + std::sync::Arc::new(parking_lot::RwLock::new(Self::default())) + } +} + +#[cfg(feature = "std")] +impl crate::traits::BareCryptoStore for KeyStore { + fn sr25519_public_keys(&self, id: KeyTypeId) -> Vec { + self.keys.get(&id) + .map(|keys| + keys.values() + .map(|s| sr25519::Pair::from_seed_slice(s).expect("`sr25519` seed slice is valid")) + .map(|p| p.public()) + .collect() + ) + .unwrap_or_default() + } + + fn sr25519_generate_new( + &mut self, + id: KeyTypeId, + seed: Option<&str>, + ) -> Result { + match seed { + Some(seed) => { + let pair = sr25519::Pair::from_string(seed, None).expect("Generates an `sr25519` pair."); + self.keys.entry(id).or_default().insert(pair.public().to_raw_vec(), pair.to_raw_vec()); + Ok(pair.public()) + }, + None => { + let (pair, _) = sr25519::Pair::generate(); + self.keys.entry(id).or_default().insert(pair.public().to_raw_vec(), pair.to_raw_vec()); + Ok(pair.public()) + } + } + } + + fn sr25519_key_pair(&self, id: KeyTypeId, pub_key: &sr25519::Public) -> Option { + self.keys.get(&id) + .and_then(|inner| + inner.get(pub_key.as_slice()) + .map(|s| sr25519::Pair::from_seed_slice(s).expect("`sr25519` seed slice is valid")) + ) + } + + fn ed25519_public_keys(&self, id: KeyTypeId) -> Vec { + self.keys.get(&id) + .map(|keys| + keys.values() + .map(|s| ed25519::Pair::from_seed_slice(s).expect("`ed25519` seed slice is valid")) + .map(|p| p.public()) + .collect() + ) + .unwrap_or_default() + } + + fn ed25519_generate_new( + &mut self, + id: KeyTypeId, + seed: Option<&str>, + ) -> Result { + match seed { + Some(seed) => { + let pair = ed25519::Pair::from_string(seed, None).expect("Generates an `ed25519` pair."); + self.keys.entry(id).or_default().insert(pair.public().to_raw_vec(), pair.to_raw_vec()); + Ok(pair.public()) + }, + None => { + let (pair, _) = ed25519::Pair::generate(); + self.keys.entry(id).or_default().insert(pair.public().to_raw_vec(), pair.to_raw_vec()); + Ok(pair.public()) + } + } + } + + fn ed25519_key_pair(&self, id: KeyTypeId, pub_key: &ed25519::Public) -> Option { + self.keys.get(&id) + .and_then(|inner| + inner.get(pub_key.as_slice()) + .map(|s| ed25519::Pair::from_seed_slice(s).expect("`ed25519` seed slice is valid")) + ) + } +} diff --git a/core/primitives/src/traits.rs b/core/primitives/src/traits.rs new file mode 100644 index 0000000000000..6fa84088db710 --- /dev/null +++ b/core/primitives/src/traits.rs @@ -0,0 +1,72 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Shareable Substrate traits. + +#[cfg(feature = "std")] +use crate::{crypto::KeyTypeId, ed25519, sr25519}; + +/// Something that generates, stores and provides access to keys. +#[cfg(feature = "std")] +pub trait BareCryptoStore: Send + Sync { + /// Returns all sr25519 public keys for the given key type. + fn sr25519_public_keys(&self, id: KeyTypeId) -> Vec; + /// Generate a new sr25519 key pair for the given key type and an optional seed. + /// + /// If the given seed is `Some(_)`, the key pair will only be stored in memory. + /// + /// Returns the public key of the generated key pair. + fn sr25519_generate_new( + &mut self, + id: KeyTypeId, + seed: Option<&str>, + ) -> Result; + /// Returns the sr25519 key pair for the given key type and public key combination. + fn sr25519_key_pair(&self, id: KeyTypeId, pub_key: &sr25519::Public) -> Option; + + /// Returns all ed25519 public keys for the given key type. + fn ed25519_public_keys(&self, id: KeyTypeId) -> Vec; + /// Generate a new ed25519 key pair for the given key type and an optional seed. + /// + /// If the given seed is `Some(_)`, the key pair will only be stored in memory. + /// + /// Returns the public key of the generated key pair. + fn ed25519_generate_new( + &mut self, + id: KeyTypeId, + seed: Option<&str>, + ) -> Result; + + /// Returns the ed25519 key pair for the given key type and public key combination. + fn ed25519_key_pair(&self, id: KeyTypeId, pub_key: &ed25519::Public) -> Option; + + /// Insert a new key. This doesn't require any known of the crypto; but a public key must be + /// manually provided. + /// + /// Places it into the file system store. + /// + /// `Err` if there's some sort of weird filesystem error, but should generally be `Ok`. + fn insert_unknown(&mut self, _key_type: KeyTypeId, _suri: &str, _public: &[u8]) -> Result<(), ()> { + Err(()) + } + + /// Get the password for this store. + fn password(&self) -> Option<&str> { None } +} + +/// A pointer to the key store. +#[cfg(feature = "std")] +pub type BareCryptoStorePtr = std::sync::Arc>; diff --git a/core/rpc/Cargo.toml b/core/rpc/Cargo.toml index cf129a03e6bca..3072ba47830dc 100644 --- a/core/rpc/Cargo.toml +++ b/core/rpc/Cargo.toml @@ -13,7 +13,7 @@ jsonrpc-core-client = "12.0.0" jsonrpc-pubsub = "12.0.0" jsonrpc-derive = "12.0.0" log = "0.4" -parking_lot = "0.8.0" +parking_lot = "0.9.0" codec = { package = "parity-scale-codec", version = "1.0.0" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" @@ -23,8 +23,9 @@ network = { package = "substrate-network", path = "../network" } primitives = { package = "substrate-primitives", path = "../primitives" } state_machine = { package = "substrate-state-machine", path = "../state-machine" } transaction_pool = { package = "substrate-transaction-pool", path = "../transaction-pool" } -sr-primitives = { path = "../sr-primitives" } +sr-primitives = { path = "../sr-primitives" } runtime_version = { package = "sr-version", path = "../sr-version" } +substrate-keystore = { path = "../keystore" } [dev-dependencies] assert_matches = "1.1" diff --git a/core/rpc/src/author/error.rs b/core/rpc/src/author/error.rs index 769f111105aad..2fcc8c780dfdb 100644 --- a/core/rpc/src/author/error.rs +++ b/core/rpc/src/author/error.rs @@ -37,6 +37,18 @@ pub enum Error { /// Incorrect extrinsic format. #[display(fmt="Invalid extrinsic format: {}", _0)] BadFormat(codec::Error), + /// Incorrect seed phrase. + #[display(fmt="Invalid seed phrase/SURI")] + BadSeedPhrase, + /// Key type ID has an unknown format. + #[display(fmt="Invalid key type ID format (should be of length four)")] + BadKeyType, + /// Key type ID has some unsupported crypto. + #[display(fmt="The crypto of key type ID is unknown")] + UnsupportedKeyType, + /// Some random issue with the key store. Shouldn't happen. + #[display(fmt="The key store is unavailable")] + KeyStoreUnavailable, } impl std::error::Error for Error { diff --git a/core/rpc/src/author/mod.rs b/core/rpc/src/author/mod.rs index aa238aeb709d8..226fd2b105cbe 100644 --- a/core/rpc/src/author/mod.rs +++ b/core/rpc/src/author/mod.rs @@ -22,7 +22,7 @@ pub mod hash; #[cfg(test)] mod tests; -use std::sync::Arc; +use std::{sync::Arc, convert::TryInto}; use client::{self, Client}; use crate::rpc::futures::{Sink, Stream, Future}; @@ -31,9 +31,12 @@ use jsonrpc_derive::rpc; use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; use log::warn; use codec::{Encode, Decode}; -use primitives::{Bytes, Blake2Hasher, H256}; +use primitives::{ + Bytes, Blake2Hasher, H256, ed25519, sr25519, crypto::{Pair, Public, key_types}, + traits::BareCryptoStorePtr +}; use sr_primitives::{generic, traits}; -use self::error::Result; +use self::error::{Error, Result}; use transaction_pool::{ txpool::{ ChainApi as PoolChainApi, @@ -57,21 +60,46 @@ pub trait AuthorApi { #[rpc(name = "author_submitExtrinsic")] fn submit_extrinsic(&self, extrinsic: Bytes) -> Result; + /// Insert a key into the keystore. + #[rpc(name = "author_insertKey")] + fn insert_key(&self, + key_type: String, + suri: String, + maybe_public: Option + ) -> Result; + /// Returns all pending extrinsics, potentially grouped by sender. #[rpc(name = "author_pendingExtrinsics")] fn pending_extrinsics(&self) -> Result>; /// Remove given extrinsic from the pool and temporarily ban it to prevent reimporting. #[rpc(name = "author_removeExtrinsic")] - fn remove_extrinsic(&self, bytes_or_hash: Vec>) -> Result>; + fn remove_extrinsic(&self, + bytes_or_hash: Vec> + ) -> Result>; /// Submit an extrinsic to watch. - #[pubsub(subscription = "author_extrinsicUpdate", subscribe, name = "author_submitAndWatchExtrinsic")] - fn watch_extrinsic(&self, metadata: Self::Metadata, subscriber: Subscriber>, bytes: Bytes); + #[pubsub( + subscription = "author_extrinsicUpdate", + subscribe, + name = "author_submitAndWatchExtrinsic" + )] + fn watch_extrinsic(&self, + metadata: Self::Metadata, + subscriber: Subscriber>, + bytes: Bytes + ); /// Unsubscribe from extrinsic watching. - #[pubsub(subscription = "author_extrinsicUpdate", unsubscribe, name = "author_unwatchExtrinsic")] - fn unwatch_extrinsic(&self, metadata: Option, id: SubscriptionId) -> Result; + #[pubsub( + subscription = "author_extrinsicUpdate", + unsubscribe, + name = "author_unwatchExtrinsic" + )] + fn unwatch_extrinsic(&self, + metadata: Option, + id: SubscriptionId + ) -> Result; } /// Authoring API @@ -82,6 +110,8 @@ pub struct Author where P: PoolChainApi + Sync + Send + 'static { pool: Arc>, /// Subscriptions manager subscriptions: Subscriptions, + /// The key store. + keystore: BareCryptoStorePtr, } impl Author where P: PoolChainApi + Sync + Send + 'static { @@ -90,11 +120,13 @@ impl Author where P: PoolChainApi + Sync + Send + 'sta client: Arc::Block, RA>>, pool: Arc>, subscriptions: Subscriptions, + keystore: BareCryptoStorePtr, ) -> Self { Author { client, pool, subscriptions, + keystore, } } } @@ -105,10 +137,38 @@ impl AuthorApi, BlockHash

> for Author whe P: PoolChainApi + Sync + Send + 'static, P::Block: traits::Block, P::Error: 'static, - RA: Send + Sync + 'static + RA: Send + Sync + 'static, { type Metadata = crate::metadata::Metadata; + fn insert_key(&self, + key_type: String, + suri: String, + maybe_public: Option, + ) -> Result { + let key_type = key_type.as_str().try_into().map_err(|_| Error::BadKeyType)?; + let mut keystore = self.keystore.write(); + let maybe_password = keystore.password(); + let public = match maybe_public { + Some(public) => public.0, + None => { + let maybe_public = match key_type { + key_types::BABE | key_types::IM_ONLINE | key_types::SR25519 => + sr25519::Pair::from_string(&suri, maybe_password) + .map(|pair| pair.public().to_raw_vec()), + key_types::GRANDPA | key_types::ED25519 => + ed25519::Pair::from_string(&suri, maybe_password) + .map(|pair| pair.public().to_raw_vec()), + _ => Err(Error::UnsupportedKeyType)?, + }; + maybe_public.map_err(|_| Error::BadSeedPhrase)? + } + }; + keystore.insert_unknown(key_type, &suri, &public[..]) + .map_err(|_| Error::KeyStoreUnavailable)?; + Ok(public.into()) + } + fn submit_extrinsic(&self, ext: Bytes) -> Result> { let xt = Decode::decode(&mut &ext[..])?; let best_block_hash = self.client.info().chain.best_hash; @@ -124,7 +184,9 @@ impl AuthorApi, BlockHash

> for Author whe Ok(self.pool.ready().map(|tx| tx.data.encode().into()).collect()) } - fn remove_extrinsic(&self, bytes_or_hash: Vec>>) -> Result>> { + fn remove_extrinsic(&self, + bytes_or_hash: Vec>> + ) -> Result>> { let hashes = bytes_or_hash.into_iter() .map(|x| match x { hash::ExtrinsicOrHash::Hash(h) => Ok(h), @@ -143,7 +205,11 @@ impl AuthorApi, BlockHash

> for Author whe ) } - fn watch_extrinsic(&self, _metadata: Self::Metadata, subscriber: Subscriber, BlockHash

>>, xt: Bytes) { + fn watch_extrinsic(&self, + _metadata: Self::Metadata, + subscriber: Subscriber, BlockHash

>>, + xt: Bytes + ) { let submit = || -> Result<_> { let best_block_hash = self.client.info().chain.best_hash; let dxt = <

::Block as traits::Block>::Extrinsic::decode(&mut &xt[..])?; diff --git a/core/rpc/src/author/tests.rs b/core/rpc/src/author/tests.rs index 629522082009f..93c39a4bc5a70 100644 --- a/core/rpc/src/author/tests.rs +++ b/core/rpc/src/author/tests.rs @@ -23,9 +23,12 @@ use transaction_pool::{ txpool::Pool, ChainApi, }; -use primitives::{H256, blake2_256, hexdisplay::HexDisplay}; +use primitives::{H256, blake2_256, hexdisplay::HexDisplay, traits::BareCryptoStore}; use test_client::{self, AccountKeyring, runtime::{Extrinsic, Transfer}}; use tokio::runtime; +use std::collections::HashMap; +use sr_primitives::KeyTypeId; +use parking_lot::RwLock; fn uxt(sender: AccountKeyring, nonce: u64) -> Extrinsic { let tx = Transfer { @@ -37,14 +40,55 @@ fn uxt(sender: AccountKeyring, nonce: u64) -> Extrinsic { tx.into_signed_tx() } +#[derive(Default)] +struct TestKeyStore { + keys: HashMap, String>>, +} + +impl BareCryptoStore for TestKeyStore { + fn sr25519_public_keys(&self, _id: KeyTypeId) -> Vec { vec![] } + fn sr25519_generate_new(&mut self, _id: KeyTypeId, _seed: Option<&str>) + -> std::result::Result + { + Err("unimplemented".into()) + } + fn sr25519_key_pair(&self, _id: KeyTypeId, _pub_key: &sr25519::Public) -> Option { + None + } + fn ed25519_public_keys(&self, _id: KeyTypeId) -> Vec { vec![] } + fn ed25519_generate_new(&mut self, _id: KeyTypeId, _seed: Option<&str>) + -> std::result::Result + { + Err("unimplemented".into()) + } + fn ed25519_key_pair(&self, _id: KeyTypeId, _pub_key: &ed25519::Public) -> Option { + None + } + + fn insert_unknown(&mut self, key_type: KeyTypeId, suri: &str, public: &[u8]) + -> std::result::Result<(), ()> + { + self.keys + .entry(key_type) + .or_default() + .insert(public.to_owned(), suri.to_owned()); + Ok(()) + } + + fn password(&self) -> Option<&str> { None } +} + #[test] fn submit_transaction_should_not_cause_error() { let runtime = runtime::Runtime::new().unwrap(); let client = Arc::new(test_client::new()); + let keystore = TestKeyStore::default(); + let keystore = Arc::new(RwLock::new(keystore)); let p = Author { client: client.clone(), pool: Arc::new(Pool::new(Default::default(), ChainApi::new(client))), subscriptions: Subscriptions::new(Arc::new(runtime.executor())), + keystore: keystore.clone(), }; let xt = uxt(AccountKeyring::Alice, 1).encode(); let h: H256 = blake2_256(&xt).into(); @@ -62,10 +106,12 @@ fn submit_transaction_should_not_cause_error() { fn submit_rich_transaction_should_not_cause_error() { let runtime = runtime::Runtime::new().unwrap(); let client = Arc::new(test_client::new()); + let keystore = Arc::new(RwLock::new(TestKeyStore::default())); let p = Author { client: client.clone(), pool: Arc::new(Pool::new(Default::default(), ChainApi::new(client.clone()))), subscriptions: Subscriptions::new(Arc::new(runtime.executor())), + keystore: keystore.clone(), }; let xt = uxt(AccountKeyring::Alice, 0).encode(); let h: H256 = blake2_256(&xt).into(); @@ -85,10 +131,12 @@ fn should_watch_extrinsic() { let mut runtime = runtime::Runtime::new().unwrap(); let client = Arc::new(test_client::new()); let pool = Arc::new(Pool::new(Default::default(), ChainApi::new(client.clone()))); + let keystore = Arc::new(RwLock::new(TestKeyStore::default())); let p = Author { client, pool: pool.clone(), subscriptions: Subscriptions::new(Arc::new(runtime.executor())), + keystore: keystore.clone(), }; let (subscriber, id_rx, data) = ::jsonrpc_pubsub::typed::Subscriber::new_test("test"); @@ -125,10 +173,12 @@ fn should_return_pending_extrinsics() { let runtime = runtime::Runtime::new().unwrap(); let client = Arc::new(test_client::new()); let pool = Arc::new(Pool::new(Default::default(), ChainApi::new(client.clone()))); + let keystore = Arc::new(RwLock::new(TestKeyStore::default())); let p = Author { client, pool: pool.clone(), subscriptions: Subscriptions::new(Arc::new(runtime.executor())), + keystore: keystore.clone(), }; let ex = uxt(AccountKeyring::Alice, 0); AuthorApi::submit_extrinsic(&p, ex.encode().into()).unwrap(); @@ -143,10 +193,12 @@ fn should_remove_extrinsics() { let runtime = runtime::Runtime::new().unwrap(); let client = Arc::new(test_client::new()); let pool = Arc::new(Pool::new(Default::default(), ChainApi::new(client.clone()))); + let keystore = Arc::new(RwLock::new(TestKeyStore::default())); let p = Author { client, pool: pool.clone(), subscriptions: Subscriptions::new(Arc::new(runtime.executor())), + keystore: keystore.clone(), }; let ex1 = uxt(AccountKeyring::Alice, 0); p.submit_extrinsic(ex1.encode().into()).unwrap(); diff --git a/core/service/Cargo.toml b/core/service/Cargo.toml index dcb47bbbfb0f4..2bcb180570ba3 100644 --- a/core/service/Cargo.toml +++ b/core/service/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" derive_more = "0.14.0" futures = "0.1.17" futures03 = { package = "futures-preview", version = "0.3.0-alpha.17", features = ["compat"] } -parking_lot = "0.8.0" +parking_lot = "0.9.0" lazy_static = "1.0" log = "0.4" slog = {version = "^2", features = ["nested-values"]} @@ -22,7 +22,9 @@ target_info = "0.1" keystore = { package = "substrate-keystore", path = "../../core/keystore" } sr-io = { path = "../../core/sr-io" } sr-primitives = { path = "../../core/sr-primitives" } -primitives = { package = "substrate-primitives", path = "../../core/primitives" } +primitives = { package = "substrate-primitives", path = "../primitives" } +app-crypto = { package = "substrate-application-crypto", path = "../application-crypto" } +substrate-session = { path = "../session" } consensus_common = { package = "substrate-consensus-common", path = "../../core/consensus/common" } network = { package = "substrate-network", path = "../../core/network" } client = { package = "substrate-client", path = "../../core/client" } @@ -40,4 +42,6 @@ substrate-test-runtime-client = { path = "../test-runtime/client" } node-executor = { path = "../../node/executor" } node-primitives = { path = "../../node/primitives" } node-runtime = { path = "../../node/runtime" } +babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../../core/consensus/babe/primitives" } grandpa = { package = "substrate-finality-grandpa", path = "../../core/finality-grandpa" } +grandpa-primitives = { package = "substrate-finality-grandpa-primitives", path = "../../core/finality-grandpa/primitives" } diff --git a/core/service/src/chain_spec.rs b/core/service/src/chain_spec.rs index ca92a4814cd51..ecd77af0829dd 100644 --- a/core/service/src/chain_spec.rs +++ b/core/service/src/chain_spec.rs @@ -34,7 +34,7 @@ enum GenesisSource { Factory(fn() -> G), } -impl Clone for GenesisSource { +impl Clone for GenesisSource { fn clone(&self) -> Self { match *self { GenesisSource::File(ref path) => GenesisSource::File(path.clone()), @@ -104,12 +104,12 @@ struct ChainSpecFile { pub type Properties = json::map::Map; /// A configuration of a chain. Can be used to build a genesis block. -pub struct ChainSpec { +pub struct ChainSpec { spec: ChainSpecFile, genesis: GenesisSource, } -impl Clone for ChainSpec { +impl Clone for ChainSpec { fn clone(&self) -> Self { ChainSpec { spec: self.spec.clone(), diff --git a/core/service/src/components.rs b/core/service/src/components.rs index 570e894c39646..47781aa79d810 100644 --- a/core/service/src/components.rs +++ b/core/service/src/components.rs @@ -19,18 +19,21 @@ use std::{sync::Arc, ops::Deref, ops::DerefMut}; use serde::{Serialize, de::DeserializeOwned}; use crate::chain_spec::ChainSpec; +use keystore::KeyStorePtr; use client_db; use client::{self, Client, runtime_api}; -use crate::{error, Service, AuthorityKeyProvider}; +use crate::{error, Service}; use consensus_common::{import_queue::ImportQueue, SelectChain}; -use network::{self, OnDemand, FinalityProofProvider, NetworkStateInfo, config::BoxFinalityProofRequestBuilder}; +use network::{ + self, OnDemand, FinalityProofProvider, NetworkStateInfo, config::BoxFinalityProofRequestBuilder +}; use substrate_executor::{NativeExecutor, NativeExecutionDispatch}; use transaction_pool::txpool::{self, Options as TransactionPoolOptions, Pool as TransactionPool}; use sr_primitives::{ BuildStorage, traits::{Block as BlockT, Header as HeaderT, ProvideRuntimeApi}, generic::BlockId }; use crate::config::Configuration; -use primitives::{Blake2Hasher, H256, Pair}; +use primitives::{Blake2Hasher, H256, traits::BareCryptoStorePtr}; use rpc::{self, apis::system::SystemInfo}; use futures::{prelude::*, future::Executor}; use futures03::channel::mpsc; @@ -129,16 +132,6 @@ pub type ComponentOffchainStorage = < /// Block type for `Components` pub type ComponentBlock = <::Factory as ServiceFactory>::Block; -/// ConsensusPair type for `Components` -pub type ComponentConsensusPair = <::Factory as ServiceFactory>::ConsensusPair; - -/// FinalityPair type for `Components` -pub type ComponentFinalityPair = <::Factory as ServiceFactory>::FinalityPair; - -/// AuthorityKeyProvider type for `Components` -pub type ComponentAuthorityKeyProvider = - AuthorityKeyProvider, ComponentConsensusPair, ComponentFinalityPair>; - /// Extrinsic hash type for `Components` pub type ComponentExHash = <::TransactionPoolApi as txpool::ChainApi>::Hash; @@ -152,6 +145,27 @@ pub type PoolApi = ::TransactionPoolApi; pub trait RuntimeGenesis: Serialize + DeserializeOwned + BuildStorage {} impl RuntimeGenesis for T {} +/// Something that can create initial session keys from given seeds. +pub trait InitialSessionKeys { + /// Generate the initial session keys for the given seeds. + fn generate_intial_session_keys( + client: Arc>, + seeds: Vec, + ) -> error::Result<()>; +} + +impl InitialSessionKeys for C where + ComponentClient: ProvideRuntimeApi, + as ProvideRuntimeApi>::Api: substrate_session::SessionKeys>, +{ + fn generate_intial_session_keys( + client: Arc>, + seeds: Vec, + ) -> error::Result<()> { + substrate_session::generate_initial_session_keys(client, seeds).map_err(Into::into) + } +} + /// Something that can start the RPC service. pub trait StartRPC { fn start_rpc( @@ -160,6 +174,7 @@ pub trait StartRPC { system_info: SystemInfo, task_executor: TaskExecutor, transaction_pool: Arc>, + keystore: KeyStorePtr, ) -> rpc::RpcHandler; } @@ -173,11 +188,12 @@ impl StartRPC for C where rpc_system_info: SystemInfo, task_executor: TaskExecutor, transaction_pool: Arc>, + keystore: KeyStorePtr, ) -> rpc::RpcHandler { let subscriptions = rpc::apis::Subscriptions::new(task_executor.clone()); let chain = rpc::apis::chain::Chain::new(client.clone(), subscriptions.clone()); let state = rpc::apis::state::State::new(client.clone(), subscriptions.clone()); - let author = rpc::apis::author::Author::new(client, transaction_pool, subscriptions); + let author = rpc::apis::author::Author::new(client, transaction_pool, subscriptions, keystore); let system = rpc::apis::system::System::new(rpc_system_info, system_send_back); rpc::rpc_handler::, ComponentExHash, _, _, _, _>( state, @@ -242,7 +258,6 @@ pub trait OffchainWorker { offchain: &offchain::OffchainWorkers< ComponentClient, ComponentOffchainStorage, - ComponentAuthorityKeyProvider, ComponentBlock >, pool: &Arc>, @@ -259,7 +274,6 @@ impl OffchainWorker for C where offchain: &offchain::OffchainWorkers< ComponentClient, ComponentOffchainStorage, - ComponentAuthorityKeyProvider, ComponentBlock >, pool: &Arc>, @@ -277,6 +291,7 @@ pub trait ServiceTrait: + StartRPC + MaintainTransactionPool + OffchainWorker + + InitialSessionKeys {} impl ServiceTrait for T where T: Deref> @@ -285,6 +300,7 @@ impl ServiceTrait for T where + StartRPC + MaintainTransactionPool + OffchainWorker + + InitialSessionKeys {} /// Alias for a an implementation of `futures::future::Executor`. @@ -294,10 +310,6 @@ pub type TaskExecutor = Arc + pub trait ServiceFactory: 'static + Sized { /// Block type. type Block: BlockT; - /// Consensus crypto type. - type ConsensusPair: Pair; - /// Finality crypto type. - type FinalityPair: Pair; /// The type that implements the runtime API. type RuntimeApi: Send + Sync; /// Network protocol extensions. @@ -412,6 +424,7 @@ pub trait Components: Sized + 'static { fn build_client( config: &FactoryFullConfiguration, executor: CodeExecutor, + keystore: Option, ) -> Result< ( Arc>, @@ -498,11 +511,11 @@ impl Components for FullComponents { fn build_client( config: &FactoryFullConfiguration, executor: CodeExecutor, - ) - -> Result<( - Arc>, - Option>>> - ), error::Error> + keystore: Option, + ) -> Result< + (Arc>, Option>>>), + error::Error, + > { let db_settings = client_db::DatabaseSettings { cache_size: config.database_cache_size.map(|u| u as usize), @@ -512,12 +525,19 @@ impl Components for FullComponents { path: config.database_path.clone(), pruning: config.pruning.clone(), }; - Ok((Arc::new(client_db::new_client( - db_settings, - executor, - &config.chain_spec, - config.execution_strategies.clone(), - )?), None)) + + Ok(( + Arc::new( + client_db::new_client( + db_settings, + executor, + &config.chain_spec, + config.execution_strategies.clone(), + keystore, + )? + ), + None, + )) } fn build_transaction_pool( @@ -600,6 +620,7 @@ impl Components for LightComponents { fn build_client( config: &FactoryFullConfiguration, executor: CodeExecutor, + _: Option, ) -> Result< ( @@ -615,9 +636,12 @@ impl Components for LightComponents { path: config.database_path.clone(), pruning: config.pruning.clone(), }; + let db_storage = client_db::light::LightStorage::new(db_settings)?; let light_blockchain = client::light::new_light_blockchain(db_storage); - let fetch_checker = Arc::new(client::light::new_fetch_checker(light_blockchain.clone(), executor.clone())); + let fetch_checker = Arc::new( + client::light::new_fetch_checker(light_blockchain.clone(), executor.clone()) + ); let fetcher = Arc::new(network::OnDemand::new(fetch_checker)); let client_backend = client::light::new_light_backend(light_blockchain, fetcher.clone()); let client = client::light::new_light(client_backend, fetcher.clone(), &config.chain_spec, executor)?; diff --git a/core/service/src/config.rs b/core/service/src/config.rs index cd2364b37d17e..b5019c82637a9 100644 --- a/core/service/src/config.rs +++ b/core/service/src/config.rs @@ -31,7 +31,7 @@ use tel::TelemetryEndpoints; /// Service configuration. #[derive(Clone)] -pub struct Configuration { +pub struct Configuration { /// Implementation name pub impl_name: &'static str, /// Implementation version @@ -45,7 +45,7 @@ pub struct Configuration { /// Network configuration. pub network: NetworkConfiguration, /// Path to key files. - pub keystore_path: Option, + pub keystore_path: PathBuf, /// Path to the database. pub database_path: PathBuf, /// Cache Size for internal database in MiB @@ -56,8 +56,6 @@ pub struct Configuration { pub state_cache_child_ratio: Option, /// Pruning settings. pub pruning: PruningMode, - /// Additional key seeds. - pub keys: Vec, /// Chain configuration. pub chain_spec: ChainSpec, /// Custom configuration. @@ -91,7 +89,13 @@ pub struct Configuration { /// running a sentry node in front of a validator, thus needing to forward GRANDPA gossip messages. pub grandpa_voter: bool, /// Node keystore's password - pub password: Protected, + pub keystore_password: Option>, + /// Development key seed. + /// + /// When running in development mode, the seed will be used to generate authority keys by the keystore. + /// + /// Should only be set when `node` is running development mode. + pub dev_key_seed: Option, } impl Configuration { @@ -111,7 +115,6 @@ impl Configuration Configuration { NetworkStatus>, NetworkState )>>>>, transaction_pool: Arc>, - keystore: ComponentAuthorityKeyProvider, - exit: ::exit_future::Exit, + exit: exit_future::Exit, signal: Option, /// Sender for futures that must be spawned as background tasks. to_spawn_tx: mpsc::UnboundedSender + Send>>, @@ -105,21 +101,22 @@ pub struct Service { _offchain_workers: Option, ComponentOffchainStorage, - ComponentAuthorityKeyProvider, ComponentBlock> >>, + keystore: keystore::KeyStorePtr, } /// Creates bare client without any networking. -pub fn new_client(config: &FactoryFullConfiguration) - -> Result>>, error::Error> -{ +pub fn new_client( + config: &FactoryFullConfiguration, +) -> Result>>, error::Error> { let executor = NativeExecutor::new(config.default_heap_pages); - let (client, _) = components::FullComponents::::build_client( + + components::FullComponents::::build_client( config, executor, - )?; - Ok(client) + None, + ).map(|r| r.0) } /// An handle for spawning tasks in the service. @@ -172,43 +169,9 @@ impl Service { // Create client let executor = NativeExecutor::new(config.default_heap_pages); - let mut keystore = if let Some(keystore_path) = config.keystore_path.as_ref() { - match Keystore::open(keystore_path.clone()) { - Ok(ks) => Some(ks), - Err(err) => { - error!("Failed to initialize keystore: {}", err); - None - } - } - } else { - None - }; - - // Keep the public key for telemetry - let public_key: String; - - // This is meant to be for testing only - // FIXME #1063 remove this - if let Some(keystore) = keystore.as_mut() { - for seed in &config.keys { - keystore.generate_from_seed::(seed)?; - keystore.generate_from_seed::(seed)?; - } - - public_key = match keystore.contents::()?.get(0) { - Some(public_key) => public_key.to_string(), - None => { - let key: ed25519::Pair = keystore.generate(&config.password.as_ref())?; - let public_key = key.public(); - info!("Generated a new keypair: {:?}", public_key); - public_key.to_string() - } - } - } else { - public_key = format!(""); - } + let keystore = Keystore::open(config.keystore_path.clone(), config.keystore_password.clone())?; - let (client, on_demand) = Components::build_client(&config, executor)?; + let (client, on_demand) = Components::build_client(&config, executor, Some(keystore.clone()))?; let select_chain = Components::build_select_chain(&mut config, client.clone())?; let (import_queue, finality_proof_request_builder) = Components::build_import_queue( &mut config, @@ -219,9 +182,16 @@ impl Service { let finality_proof_provider = Components::build_finality_proof_provider(client.clone())?; let chain_info = client.info().chain; + Components::RuntimeServices::generate_intial_session_keys( + client.clone(), + config.dev_key_seed.clone().map(|s| vec![s]).unwrap_or_default(), + )?; + let version = config.full_version(); info!("Highest known block at #{}", chain_info.best_number); - telemetry!(SUBSTRATE_INFO; "node.start"; + telemetry!( + SUBSTRATE_INFO; + "node.start"; "height" => chain_info.best_number.saturated_into::(), "best" => ?chain_info.best_hash ); @@ -234,7 +204,7 @@ impl Service { imports_external_transactions: !config.roles.is_light(), pool: transaction_pool.clone(), client: client.clone(), - }); + }); let protocol_id = { let protocol_id_full = match config.chain_spec.protocol_id() { @@ -267,23 +237,11 @@ impl Service { let network = network_mut.service().clone(); let network_status_sinks = Arc::new(Mutex::new(Vec::new())); - let keystore_authority_key = AuthorityKeyProvider { - _marker: PhantomData, - roles: config.roles, - password: config.password.clone(), - keystore: keystore.map(Arc::new), - }; - #[allow(deprecated)] let offchain_storage = client.backend().offchain_storage(); let offchain_workers = match (config.offchain_worker, offchain_storage) { (true, Some(db)) => { - Some(Arc::new(offchain::OffchainWorkers::new( - client.clone(), - db, - keystore_authority_key.clone(), - config.password.clone(), - ))) + Some(Arc::new(offchain::OffchainWorkers::new(client.clone(), db))) }, (true, None) => { log::warn!("Offchain workers disabled, due to lack of offchain storage support in backend."); @@ -421,6 +379,7 @@ impl Service { system_info.clone(), Arc::new(SpawnTaskHandle { sender: to_spawn_tx.clone() }), transaction_pool.clone(), + keystore.clone(), ) }; let rpc_handlers = gen_handler(); @@ -465,7 +424,6 @@ impl Service { "version" => version.clone(), "config" => "", "chain" => chain_name.clone(), - "pubkey" => &public_key, "authority" => is_authority, "network_id" => network_id.clone() ); @@ -491,7 +449,6 @@ impl Service { to_spawn_tx, to_spawn_rx, to_poll: Vec::new(), - keystore: keystore_authority_key, config, exit, rpc_handlers, @@ -499,28 +456,20 @@ impl Service { _telemetry: telemetry, _offchain_workers: offchain_workers, _telemetry_on_connect_sinks: telemetry_connection_sinks.clone(), + keystore, }) } - /// give the authority key, if we are an authority and have a key - pub fn authority_key(&self) -> Option> { - use offchain::AuthorityKeyProvider; - - self.keystore.authority_key(&BlockId::Number(Zero::zero())) - } - - /// give the authority key, if we are an authority and have a key - pub fn fg_authority_key(&self) -> Option> { - use offchain::AuthorityKeyProvider; - - self.keystore.fg_authority_key(&BlockId::Number(Zero::zero())) - } - - /// return a shared instance of Telemetry (if enabled) + /// Return a shared instance of Telemetry (if enabled) pub fn telemetry(&self) -> Option { self._telemetry.as_ref().map(|t| t.clone()) } + /// Returns the keystore instance. + pub fn keystore(&self) -> keystore::KeyStorePtr { + self.keystore.clone() + } + /// Spawns a task in the background that runs the future passed as parameter. pub fn spawn_task(&self, task: impl Future + Send + 'static) { let _ = self.to_spawn_tx.unbounded_send(Box::new(task)); @@ -910,73 +859,6 @@ impl network::TransactionPool, ComponentBlock< } } -#[derive(Clone)] -/// A provider of current authority key. -pub struct AuthorityKeyProvider { - _marker: PhantomData<(Block, ConsensusPair, FinalityPair)>, - roles: Roles, - keystore: Option>, - password: crypto::Protected, -} - -impl - offchain::AuthorityKeyProvider - for AuthorityKeyProvider -where - Block: sr_primitives::traits::Block, - ConsensusPair: Pair, - FinalityPair: Pair, -{ - type ConsensusPair = ConsensusPair; - type FinalityPair = FinalityPair; - - fn authority_key(&self, _at: &BlockId) -> Option { - if self.roles != Roles::AUTHORITY { - return None - } - - let keystore = match self.keystore { - Some(ref keystore) => keystore, - None => return None - }; - - let loaded_key = keystore - .contents() - .map(|keys| keys.get(0) - .map(|k| keystore.load(k, self.password.as_ref())) - ); - - if let Ok(Some(Ok(key))) = loaded_key { - Some(key) - } else { - None - } - } - - fn fg_authority_key(&self, _at: &BlockId) -> Option { - if self.roles != Roles::AUTHORITY { - return None - } - - let keystore = match self.keystore { - Some(ref keystore) => keystore, - None => return None - }; - - let loaded_key = keystore - .contents() - .map(|keys| keys.get(0) - .map(|k| keystore.load(k, self.password.as_ref())) - ); - - if let Ok(Some(Ok(key))) = loaded_key { - Some(key) - } else { - None - } - } -} - /// Constructs a service factory with the given name that implements the `ServiceFactory` trait. /// The required parameters are required to be given in the exact order. Some parameters are followed /// by `{}` blocks. These blocks are required and used to initialize the given parameter. @@ -998,6 +880,8 @@ where /// # use node_runtime::{GenesisConfig, RuntimeApi}; /// # use std::sync::Arc; /// # use node_primitives::Block; +/// # use babe_primitives::AuthorityPair as BabePair; +/// # use grandpa_primitives::AuthorityPair as GrandpaPair; /// # use sr_primitives::Justification; /// # use sr_primitives::traits::Block as BlockT; /// # use grandpa; @@ -1025,8 +909,6 @@ where /// struct Factory { /// // Declare the block type /// Block = Block, -/// ConsensusPair = primitives::ed25519::Pair, -/// FinalityPair = primitives::ed25519::Pair, /// RuntimeApi = RuntimeApi, /// // Declare the network protocol and give an initializer. /// NetworkProtocol = NodeProtocol { |config| Ok(NodeProtocol::new()) }, @@ -1070,8 +952,6 @@ macro_rules! construct_service_factory { $(#[$attr:meta])* struct $name:ident { Block = $block:ty, - ConsensusPair = $consensus_pair:ty, - FinalityPair = $finality_pair:ty, RuntimeApi = $runtime_api:ty, NetworkProtocol = $protocol:ty { $( $protocol_init:tt )* }, RuntimeDispatch = $dispatch:ty, @@ -1097,8 +977,6 @@ macro_rules! construct_service_factory { #[allow(unused_variables)] impl $crate::ServiceFactory for $name { type Block = $block; - type ConsensusPair = $consensus_pair; - type FinalityPair = $finality_pair; type RuntimeApi = $runtime_api; type NetworkProtocol = $protocol; type RuntimeDispatch = $dispatch; diff --git a/core/service/test/src/lib.rs b/core/service/test/src/lib.rs index 8375521a70210..b9f05af2c7fa1 100644 --- a/core/service/test/src/lib.rs +++ b/core/service/test/src/lib.rs @@ -135,10 +135,6 @@ fn node_config ( ) -> FactoryFullConfiguration { let root = root.path().join(format!("node-{}", index)); - let mut keys = Vec::new(); - if let Some(seed) = key_seed { - keys.push(seed); - } let config_path = Some(String::from(root.join("network").to_str().unwrap())); let net_config_path = config_path.clone(); @@ -173,13 +169,13 @@ fn node_config ( roles: role, transaction_pool: Default::default(), network: network_config, - keystore_path: Some(root.join("key")), + keystore_path: root.join("key"), + keystore_password: None, database_path: root.join("db"), database_cache_size: None, state_cache_size: 16777216, state_cache_child_ratio: None, pruning: Default::default(), - keys: keys, chain_spec: (*spec).clone(), custom: Default::default(), name: format!("Node {}", index), @@ -195,7 +191,7 @@ fn node_config ( force_authoring: false, disable_grandpa: false, grandpa_voter: false, - password: "".to_string().into(), + dev_key_seed: key_seed, } } diff --git a/core/session/Cargo.toml b/core/session/Cargo.toml new file mode 100644 index 0000000000000..5d8cb3f0001ba --- /dev/null +++ b/core/session/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "substrate-session" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +client = { package = "substrate-client", path = "../client", default-features = false } +rstd = { package = "sr-std", path = "../sr-std", default-features = false } +sr-primitives = { path = "../sr-primitives", optional = true } +primitives = { package = "substrate-primitives", path = "../primitives", optional = true } + +[features] +default = [ "std" ] +std = [ "client/std", "rstd/std", "sr-primitives", "primitives" ] diff --git a/core/session/src/lib.rs b/core/session/src/lib.rs new file mode 100644 index 0000000000000..a962f2bfe31b9 --- /dev/null +++ b/core/session/src/lib.rs @@ -0,0 +1,64 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Substrate core types around sessions. + +#![cfg_attr(not(feature = "std"), no_std)] + +use rstd::vec::Vec; + +#[cfg(feature = "std")] +use sr_primitives::traits::{ProvideRuntimeApi, Block as BlockT}; +#[cfg(feature = "std")] +use primitives::{H256, Blake2Hasher}; + +client::decl_runtime_apis! { + /// Session keys runtime api. + pub trait SessionKeys { + /// Generate a set of session keys with optionally using the given seed. + /// + /// The seed needs to be a valid `utf8` string. + /// + /// Returns the concatenated SCALE encoded public keys. + fn generate_session_keys(seed: Option>) -> Vec; + } +} + +/// Generate the initial session keys with the given seeds. +#[cfg(feature = "std")] +pub fn generate_initial_session_keys( + client: std::sync::Arc>, + seeds: Vec, +) -> Result<(), client::error::Error> +where + B: client::backend::Backend, + E: client::CallExecutor, + Block: BlockT, + client::Client: ProvideRuntimeApi, + as ProvideRuntimeApi>::Api: SessionKeys, +{ + let info = client.info().chain; + let runtime_api = client.runtime_api(); + + for seed in seeds { + runtime_api.generate_session_keys( + &sr_primitives::generic::BlockId::Number(info.best_number), + Some(seed.as_bytes().to_vec()), + )?; + } + + Ok(()) +} \ No newline at end of file diff --git a/core/sr-api-macros/tests/runtime_calls.rs b/core/sr-api-macros/tests/runtime_calls.rs index 75da2fe292889..6b79e52ee21d0 100644 --- a/core/sr-api-macros/tests/runtime_calls.rs +++ b/core/sr-api-macros/tests/runtime_calls.rs @@ -190,5 +190,6 @@ fn record_proof_works() { &executor, "Core_execute_block", &block.encode(), + None, ).expect("Executes block while using the proof backend"); } diff --git a/core/sr-io/src/lib.rs b/core/sr-io/src/lib.rs index ac4799513d377..7227ef4f0d3df 100644 --- a/core/sr-io/src/lib.rs +++ b/core/sr-io/src/lib.rs @@ -33,12 +33,11 @@ use rstd::vec::Vec; pub use codec; pub use primitives::Blake2Hasher; -use primitives::offchain::{ - Timestamp, - HttpRequestId, HttpRequestStatus, HttpError, - CryptoKind, CryptoKey, - StorageKind, - OpaqueNetworkState, +use primitives::{ + crypto::KeyTypeId, ed25519, sr25519, + offchain::{ + Timestamp, HttpRequestId, HttpRequestStatus, HttpError, StorageKind, OpaqueNetworkState, + }, }; /// Error verifying ECDSA signature @@ -69,7 +68,7 @@ macro_rules! export_api { $( #[$attr:meta] )* fn $name:ident $(< $( $g_name:ident $( : $g_ty:path )? ),+ >)? - ( $( $arg:ident : $arg_ty:ty ),* ) + ( $( $arg:ident : $arg_ty:ty ),* $(,)? ) $( -> $ret:ty )? $( where $( $w_name:path : $w_ty:path ),+ )?; )* @@ -200,11 +199,45 @@ export_api! { export_api! { pub(crate) trait CryptoApi { - /// Verify a ed25519 signature. - fn ed25519_verify>(sig: &[u8; 64], msg: &[u8], pubkey: P) -> bool; + /// Returns all ed25519 public keys for the given key id from the keystore. + fn ed25519_public_keys(id: KeyTypeId) -> Vec; + /// Generate an ed22519 key for the given key type and store it in the keystore. + /// + /// Returns the raw public key. + fn ed25519_generate(id: KeyTypeId, seed: Option<&str>) -> ed25519::Public; + /// Sign the given `msg` with the ed25519 key that corresponds to the given public key and + /// key type in the keystore. + /// + /// Returns the raw signature. + fn ed25519_sign>( + id: KeyTypeId, + pubkey: &ed25519::Public, + msg: &M, + ) -> Option; + /// Verify an ed25519 signature. + /// + /// Returns `true` when the verification in successful. + fn ed25519_verify(sig: &ed25519::Signature, msg: &[u8], pubkey: &ed25519::Public) -> bool; + /// Returns all sr25519 public keys for the given key id from the keystore. + fn sr25519_public_keys(id: KeyTypeId) -> Vec; + /// Generate an sr22519 key for the given key type and store it in the keystore. + /// + /// Returns the raw public key. + fn sr25519_generate(id: KeyTypeId, seed: Option<&str>) -> sr25519::Public; + /// Sign the given `msg` with the sr25519 key that corresponds to the given public key and + /// key type in the keystore. + /// + /// Returns the raw signature. + fn sr25519_sign>( + id: KeyTypeId, + pubkey: &sr25519::Public, + msg: &M, + ) -> Option; /// Verify an sr25519 signature. - fn sr25519_verify>(sig: &[u8; 64], msg: &[u8], pubkey: P) -> bool; + /// + /// Returns `true` when the verification in successful. + fn sr25519_verify(sig: &sr25519::Signature, msg: &[u8], pubkey: &sr25519::Public) -> bool; /// Verify and recover a SECP256k1 ECDSA signature. /// - `sig` is passed in RSV format. V should be either 0/1 or 27/28. @@ -245,42 +278,6 @@ export_api! { /// Returns information about the local node's network state. fn network_state() -> Result; - /// Returns the currently configured authority public key, if available. - fn pubkey(key: CryptoKey) -> Result, ()>; - - /// Create new key(pair) for signing/encryption/decryption. - /// - /// Returns an error if given crypto kind is not supported. - fn new_crypto_key(crypto: CryptoKind) -> Result; - - /// Encrypt a piece of data using given crypto key. - /// - /// If `key` is `None`, it will attempt to use current authority key. - /// - /// Returns an error if `key` is not available or does not exist. - fn encrypt(key: CryptoKey, data: &[u8]) -> Result, ()>; - - /// Decrypt a piece of data using given crypto key. - /// - /// If `key` is `None`, it will attempt to use current authority key. - /// - /// Returns an error if data cannot be decrypted or the `key` is not available or does not exist. - fn decrypt(key: CryptoKey, data: &[u8]) -> Result, ()>; - - /// Sign a piece of data using given crypto key. - /// - /// If `key` is `None`, it will attempt to use current authority key. - /// - /// Returns an error if `key` is not available or does not exist. - fn sign(key: CryptoKey, data: &[u8]) -> Result, ()>; - - /// Verifies that `signature` for `msg` matches given `key`. - /// - /// Returns an `Ok` with `true` in case it does, `false` in case it doesn't. - /// Returns an error in case the key is not available or does not exist or the parameters - /// lengths are incorrect. - fn verify(key: CryptoKey, msg: &[u8], signature: &[u8]) -> Result; - /// Returns current UNIX timestamp (in millis) fn timestamp() -> Timestamp; diff --git a/core/sr-io/with_std.rs b/core/sr-io/with_std.rs index 9413673593169..8774412c1cb1b 100644 --- a/core/sr-io/with_std.rs +++ b/core/sr-io/with_std.rs @@ -15,24 +15,19 @@ // along with Substrate. If not, see . use primitives::{ - blake2_128, blake2_256, twox_128, twox_256, twox_64, ed25519, Blake2Hasher, - sr25519, Pair + blake2_128, blake2_256, twox_128, twox_256, twox_64, ed25519, Blake2Hasher, sr25519, Pair, }; // Switch to this after PoC-3 // pub use primitives::BlakeHasher; pub use substrate_state_machine::{ - Externalities, - BasicExternalities, - TestExternalities, - ChildStorageKey + Externalities, BasicExternalities, TestExternalities, ChildStorageKey, }; use environmental::environmental; use primitives::{offchain, hexdisplay::HexDisplay, H256}; use trie::{TrieConfiguration, trie_types::Layout}; -#[cfg(feature = "std")] -use std::collections::HashMap; +use std::{collections::HashMap, convert::TryFrom}; environmental!(ext: trait Externalities); @@ -208,12 +203,82 @@ impl OtherApi for () { } impl CryptoApi for () { - fn ed25519_verify>(sig: &[u8; 64], msg: &[u8], pubkey: P) -> bool { - ed25519::Pair::verify_weak(sig, msg, pubkey) + fn ed25519_public_keys(id: KeyTypeId) -> Vec { + ext::with(|ext| { + ext.keystore() + .expect("No `keystore` associated for the current context!") + .write() + .ed25519_public_keys(id) + }).expect("`ed25519_public_keys` cannot be called outside of an Externalities-provided environment.") + } + + fn ed25519_generate(id: KeyTypeId, seed: Option<&str>) -> ed25519::Public { + ext::with(|ext| { + ext.keystore() + .expect("No `keystore` associated for the current context!") + .write() + .ed25519_generate_new(id, seed) + .expect("`ed25519_generate` failed") + }).expect("`ed25519_generate` cannot be called outside of an Externalities-provided environment.") + } + + fn ed25519_sign>( + id: KeyTypeId, + pubkey: &ed25519::Public, + msg: &M, + ) -> Option { + let pub_key = ed25519::Public::try_from(pubkey.as_ref()).ok()?; + + ext::with(|ext| { + ext.keystore() + .expect("No `keystore` associated for the current context!") + .read() + .ed25519_key_pair(id, &pub_key) + .map(|k| k.sign(msg.as_ref()).into()) + }).expect("`ed25519_sign` cannot be called outside of an Externalities-provided environment.") + } + + fn ed25519_verify(sig: &ed25519::Signature, msg: &[u8], pubkey: &ed25519::Public) -> bool { + ed25519::Pair::verify(sig, msg, pubkey) + } + + fn sr25519_public_keys(id: KeyTypeId) -> Vec { + ext::with(|ext| { + ext.keystore() + .expect("No `keystore` associated for the current context!") + .write() + .sr25519_public_keys(id) + }).expect("`sr25519_public_keys` cannot be called outside of an Externalities-provided environment.") + } + + fn sr25519_generate(id: KeyTypeId, seed: Option<&str>) -> sr25519::Public { + ext::with(|ext| { + ext.keystore() + .expect("No `keystore` associated for the current context!") + .write() + .sr25519_generate_new(id, seed) + .expect("`sr25519_generate` failed") + }).expect("`sr25519_generate` cannot be called outside of an Externalities-provided environment.") + } + + fn sr25519_sign>( + id: KeyTypeId, + pubkey: &sr25519::Public, + msg: &M, + ) -> Option { + let pub_key = sr25519::Public::try_from(pubkey.as_ref()).ok()?; + + ext::with(|ext| { + ext.keystore() + .expect("No `keystore` associated for the current context!") + .read() + .sr25519_key_pair(id, &pub_key) + .map(|k| k.sign(msg.as_ref()).into()) + }).expect("`sr25519_sign` cannot be called outside of an Externalities-provided environment.") } - fn sr25519_verify>(sig: &[u8; 64], msg: &[u8], pubkey: P) -> bool { - sr25519::Pair::verify_weak(sig, msg, pubkey) + fn sr25519_verify(sig: &sr25519::Signature, msg: &[u8], pubkey: &sr25519::Public) -> bool { + sr25519::Pair::verify(sig, msg, pubkey) } fn secp256k1_ecdsa_recover(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 64], EcdsaVerifyError> { @@ -276,55 +341,6 @@ impl OffchainApi for () { }, "network_state can be called only in the offchain worker context") } - fn pubkey(key: offchain::CryptoKey) -> Result, ()> { - with_offchain(|ext| { - ext.pubkey(key) - }, "authority_pubkey can be called only in the offchain worker context") - } - - fn new_crypto_key(crypto: offchain::CryptoKind) -> Result { - with_offchain(|ext| { - ext.new_crypto_key(crypto) - }, "new_crypto_key can be called only in the offchain worker context") - } - - fn encrypt( - key: offchain::CryptoKey, - data: &[u8], - ) -> Result, ()> { - with_offchain(|ext| { - ext.encrypt(key, data) - }, "encrypt can be called only in the offchain worker context") - } - - fn decrypt( - key: offchain::CryptoKey, - data: &[u8], - ) -> Result, ()> { - with_offchain(|ext| { - ext.decrypt(key, data) - }, "decrypt can be called only in the offchain worker context") - } - - fn sign( - key: offchain::CryptoKey, - data: &[u8], - ) -> Result, ()> { - with_offchain(|ext| { - ext.sign(key, data) - }, "sign can be called only in the offchain worker context") - } - - fn verify( - key: offchain::CryptoKey, - msg: &[u8], - signature: &[u8], - ) -> Result { - with_offchain(|ext| { - ext.verify(key, msg, signature) - }, "verify can be called only in the offchain worker context") - } - fn timestamp() -> offchain::Timestamp { with_offchain(|ext| { ext.timestamp() diff --git a/core/sr-io/without_std.rs b/core/sr-io/without_std.rs index 4dc28f0471292..09b3cc0e2e32c 100644 --- a/core/sr-io/without_std.rs +++ b/core/sr-io/without_std.rs @@ -19,8 +19,9 @@ pub use rstd; pub use rstd::{mem, slice}; use core::{intrinsics, panic::PanicInfo}; -use rstd::{vec::Vec, cell::Cell, convert::TryInto, convert::TryFrom}; +use rstd::{vec::Vec, cell::Cell, convert::TryInto}; use primitives::{offchain, Blake2Hasher}; +use codec::Decode; #[cfg(not(feature = "no_panic_handler"))] #[panic_handler] @@ -158,7 +159,7 @@ pub mod ext { ( $( $( #[$attr:meta] )* - fn $name:ident ( $( $arg:ident : $arg_ty:ty ),* ) $( -> $ret:ty )?; + fn $name:ident ( $( $arg:ident : $arg_ty:ty ),* $(,)? ) $( -> $ret:ty )?; )* ) => { $( @@ -352,25 +353,72 @@ pub mod ext { fn ext_twox_256(data: *const u8, len: u32, out: *mut u8); /// Keccak256 hash fn ext_keccak_256(data: *const u8, len: u32, out: *mut u8); - /// Note: ext_ed25519_verify returns 0 if the signature is correct, nonzero otherwise. + + /// Returns all `ed25519` public keys for the given key type from the keystore. + fn ext_ed25519_public_keys(id: *const u8, result_len: *mut u32) -> *mut u8; + + /// Note: `ext_ed25519_verify` returns `0` if the signature is correct, nonzero otherwise. fn ext_ed25519_verify( msg_data: *const u8, msg_len: u32, sig_data: *const u8, - pubkey_data: *const u8 + pubkey_data: *const u8, + ) -> u32; + + /// Generate an `ed25519` key pair for the given key type id and store the public key + /// in `out`. + fn ext_ed25519_generate(id: *const u8, seed: *const u8, seed_len: u32, out: *mut u8); + + /// Sign the given `msg` with the `ed25519` key pair that corresponds to then given key + /// type id and public key. The raw signature is stored in `out`. + /// + /// # Returns + /// + /// - `0` on success + /// - nonezero if something failed, e.g. retrieving of the key. + fn ext_ed25519_sign( + id: *const u8, + pubkey: *const u8, + msg: *const u8, + msg_len: u32, + out: *mut u8, ) -> u32; - /// Note: ext_sr25519_verify returns 0 if the signature is correct, nonzero otherwise. + + /// Returns all `sr25519` public keys for the given key type from the keystore. + fn ext_sr25519_public_keys(id: *const u8, result_len: *mut u32) -> *mut u8; + + /// Note: `ext_sr25519_verify` returns 0 if the signature is correct, nonzero otherwise. fn ext_sr25519_verify( msg_data: *const u8, msg_len: u32, sig_data: *const u8, - pubkey_data: *const u8 + pubkey_data: *const u8, ) -> u32; + + /// Generate an `sr25519` key pair for the given key type id and store the public + /// key in `out`. + fn ext_sr25519_generate(id: *const u8, seed: *const u8, seed_len: u32, out: *mut u8); + + /// Sign the given `msg` with the `sr25519` key pair that corresponds to then given key + /// type id and public key. The raw signature is stored in `out`. + /// + /// # Returns + /// + /// - `0` on success + /// - nonezero if something failed, e.g. retrieving of the key. + fn ext_sr25519_sign( + id: *const u8, + pubkey: *const u8, + msg: *const u8, + msg_len: u32, + out: *mut u8, + ) -> u32; + /// Note: ext_secp256k1_ecdsa_recover returns 0 if the signature is correct, nonzero otherwise. fn ext_secp256k1_ecdsa_recover( msg_data: *const u8, sig_data: *const u8, - pubkey_data: *mut u8 + pubkey_data: *mut u8, ) -> u32; //================================ @@ -398,94 +446,6 @@ pub mod ext { /// runtime code can always rely on it. fn ext_network_state(written_out: *mut u32) -> *mut u8; - /// Returns the locally configured authority public key, if available. - /// The `crypto` argument is `offchain::CryptoKind` converted to `u32`. - /// - /// # Returns - /// - /// The encoded `Result, ()>`. - /// `written_out` contains the length of the message. - /// - /// The ownership of the returned buffer is transferred to the runtime - /// code and the runtime is responsible for freeing it. This is always - /// a properly allocated pointer (which cannot be NULL), hence the - /// runtime code can always rely on it. - fn ext_pubkey(key: u64, written_out: *mut u32) -> *mut u8; - - /// Create new key(pair) for signing/encryption/decryption. - /// - /// # Returns - /// - /// - A crypto key id (if the value is less than u16::max_value) - /// - `u32::max_value` in case the crypto is not supported - fn ext_new_crypto_key(crypto: u32) -> u64; - - /// Encrypt a piece of data using given crypto key. - /// - /// If `key` is `0`, it will attempt to use current authority key of given `kind`. - /// - /// # Returns - /// - /// - `0` in case the key is invalid, `msg_len` is set to `u32::max_value` - /// - Otherwise, pointer to the encrypted message in memory, - /// `msg_len` contains the length of the message. - fn ext_encrypt( - key: u64, - data: *const u8, - data_len: u32, - msg_len: *mut u32 - ) -> *mut u8; - - /// Decrypt a piece of data using given crypto key. - /// - /// If `key` is `0`, it will attempt to use current authority key of given `kind`. - /// - /// # Returns - /// - /// - `0` in case the key is invalid or data couldn't be decrypted, - /// `msg_len` is set to `u32::max_value` - /// - Otherwise, pointer to the decrypted message in memory, - /// `msg_len` contains the length of the message. - fn ext_decrypt( - key: u64, - data: *const u8, - data_len: u32, - msg_len: *mut u32 - ) -> *mut u8; - - /// Sign a piece of data using given crypto key. - /// - /// If `key` is `0`, it will attempt to use current authority key of given `kind`. - /// - /// # Returns - /// - /// - `0` in case the key is invalid, - /// `sig_data_len` is set to `u32::max_value` - /// - Otherwise, pointer to the signature in memory, - /// `sig_data_len` contains the length of the signature. - fn ext_sign( - key: u64, - data: *const u8, - data_len: u32, - sig_data_len: *mut u32 - ) -> *mut u8; - - /// Verifies that `signature` for `msg` matches given `key`. - /// - /// If `key` is `0`, it will attempt to use current authority key of given `kind`. - /// - /// # Returns - /// - `0` in case the signature is correct - /// - `1` in case it doesn't match the key - /// - `u32::max_value` if the key is invalid. - fn ext_verify( - key: u64, - msg: *const u8, - msg_len: u32, - signature: *const u8, - signature_len: u32 - ) -> u32; - /// Returns current UNIX timestamp (milliseconds) fn ext_timestamp() -> u64; @@ -871,15 +831,105 @@ impl HashingApi for () { } impl CryptoApi for () { - fn ed25519_verify>(sig: &[u8; 64], msg: &[u8], pubkey: P) -> bool { + fn ed25519_public_keys(id: KeyTypeId) -> Vec { + let mut res_len = 0u32; unsafe { - ext_ed25519_verify.get()(msg.as_ptr(), msg.len() as u32, sig.as_ptr(), pubkey.as_ref().as_ptr()) == 0 + let res_ptr = ext_ed25519_public_keys.get()(id.0.as_ptr(), &mut res_len); + Vec::decode(&mut rstd::slice::from_raw_parts(res_ptr, res_len as usize)).unwrap_or_default() } } - fn sr25519_verify>(sig: &[u8; 64], msg: &[u8], pubkey: P) -> bool { + fn ed25519_generate(id: KeyTypeId, seed: Option<&str>) -> ed25519::Public { + let mut res = [0u8; 32]; + let seed = seed.as_ref().map(|s| s.as_bytes()).unwrap_or(&[]); unsafe { - ext_sr25519_verify.get()(msg.as_ptr(), msg.len() as u32, sig.as_ptr(), pubkey.as_ref().as_ptr()) == 0 + ext_ed25519_generate.get()(id.0.as_ptr(), seed.as_ptr(), seed.len() as u32, res.as_mut_ptr()) + }; + ed25519::Public(res) + } + + fn ed25519_sign>( + id: KeyTypeId, + pubkey: &ed25519::Public, + msg: &M, + ) -> Option { + let mut res = [0u8; 64]; + let success = unsafe { + ext_ed25519_sign.get()( + id.0.as_ptr(), + pubkey.0.as_ptr(), + msg.as_ref().as_ptr(), + msg.as_ref().len() as u32, + res.as_mut_ptr(), + ) == 0 + }; + + if success { + Some(ed25519::Signature(res)) + } else { + None + } + } + + fn ed25519_verify(sig: &ed25519::Signature, msg: &[u8], pubkey: &ed25519::Public) -> bool { + unsafe { + ext_ed25519_verify.get()( + msg.as_ptr(), + msg.len() as u32, + sig.0.as_ptr(), + pubkey.0.as_ptr(), + ) == 0 + } + } + + fn sr25519_public_keys(id: KeyTypeId) -> Vec { + let mut res_len = 0u32; + unsafe { + let res_ptr = ext_sr25519_public_keys.get()(id.0.as_ptr(), &mut res_len); + Vec::decode(&mut rstd::slice::from_raw_parts(res_ptr, res_len as usize)).unwrap_or_default() + } + } + + fn sr25519_generate(id: KeyTypeId, seed: Option<&str>) -> sr25519::Public { + let mut res = [0u8;32]; + let seed = seed.as_ref().map(|s| s.as_bytes()).unwrap_or(&[]); + unsafe { + ext_sr25519_generate.get()(id.0.as_ptr(), seed.as_ptr(), seed.len() as u32, res.as_mut_ptr()) + }; + sr25519::Public(res) + } + + fn sr25519_sign>( + id: KeyTypeId, + pubkey: &sr25519::Public, + msg: &M, + ) -> Option { + let mut res = [0u8; 64]; + let success = unsafe { + ext_sr25519_sign.get()( + id.0.as_ptr(), + pubkey.0.as_ptr(), + msg.as_ref().as_ptr(), + msg.as_ref().len() as u32, + res.as_mut_ptr(), + ) == 0 + }; + + if success { + Some(sr25519::Signature(res)) + } else { + None + } + } + + fn sr25519_verify(sig: &sr25519::Signature, msg: &[u8], pubkey: &sr25519::Public) -> bool { + unsafe { + ext_sr25519_verify.get()( + msg.as_ptr(), + msg.len() as u32, + sig.0.as_ptr(), + pubkey.0.as_ptr(), + ) == 0 } } @@ -925,104 +975,6 @@ impl OffchainApi for () { } } - fn pubkey(key: CryptoKey) -> Result, ()> { - let mut len = 0u32; - let raw_result = unsafe { - let ptr = ext_pubkey.get()( - key.into(), - &mut len, - ); - - from_raw_parts(ptr, len) - }; - - match raw_result { - Some(raw_result) => codec::Decode::decode(&mut &*raw_result).unwrap_or(Err(())), - None => Err(()) - } - } - - fn new_crypto_key(crypto: offchain::CryptoKind) -> Result { - let crypto = crypto.into(); - let ret = unsafe { - ext_new_crypto_key.get()(crypto) - }; - offchain::CryptoKey::try_from(ret) - } - - fn encrypt( - key: offchain::CryptoKey, - data: &[u8], - ) -> Result, ()> { - let mut len = 0_u32; - unsafe { - let ptr = ext_encrypt.get()( - key.into(), - data.as_ptr(), - data.len() as u32, - &mut len - ); - - from_raw_parts(ptr, len).ok_or(()) - } - } - - fn decrypt( - key: offchain::CryptoKey, - data: &[u8], - ) -> Result, ()> { - let mut len = 0_u32; - unsafe { - let ptr = ext_decrypt.get()( - key.into(), - data.as_ptr(), - data.len() as u32, - &mut len - ); - - from_raw_parts(ptr, len).ok_or(()) - } - } - - fn sign( - key: offchain::CryptoKey, - data: &[u8], - ) -> Result, ()> { - let mut len = 0_u32; - unsafe { - let ptr = ext_sign.get()( - key.into(), - data.as_ptr(), - data.len() as u32, - &mut len - ); - - from_raw_parts(ptr, len).ok_or(()) - } - } - - fn verify( - key: offchain::CryptoKey, - msg: &[u8], - signature: &[u8], - ) -> Result { - let val = unsafe { - ext_verify.get()( - key.into(), - msg.as_ptr(), - msg.len() as u32, - signature.as_ptr(), - signature.len() as u32, - ) - }; - - match val { - 0 => Ok(true), - 1 => Ok(false), - _ => Err(()), - } - } - fn timestamp() -> offchain::Timestamp { offchain::Timestamp::from_unix_millis(unsafe { ext_timestamp.get()() diff --git a/core/sr-primitives/Cargo.toml b/core/sr-primitives/Cargo.toml index e161d5b8874c8..d3510e6baa834 100644 --- a/core/sr-primitives/Cargo.toml +++ b/core/sr-primitives/Cargo.toml @@ -10,10 +10,12 @@ integer-sqrt = { version = "0.1.2" } serde = { version = "1.0", optional = true, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } primitives = { package = "substrate-primitives", path = "../primitives", default-features = false } +app-crypto = { package = "substrate-application-crypto", path = "../application-crypto", default-features = false } rstd = { package = "sr-std", path = "../sr-std", default-features = false } runtime_io = { package = "sr-io", path = "../sr-io", default-features = false } log = { version = "0.4", optional = true } paste = { version = "0.1"} +rand = { version = "0.7.0", optional = true } [dev-dependencies] serde_json = "1.0" @@ -29,4 +31,6 @@ std = [ "runtime_io/std", "codec/std", "primitives/std", + "app-crypto/std", + "rand", ] diff --git a/core/sr-primitives/src/generic/checked_extrinsic.rs b/core/sr-primitives/src/generic/checked_extrinsic.rs index 04ccd1162c6c6..44a0758267468 100644 --- a/core/sr-primitives/src/generic/checked_extrinsic.rs +++ b/core/sr-primitives/src/generic/checked_extrinsic.rs @@ -45,11 +45,10 @@ for where AccountId: Member + MaybeDisplay, Call: Member + Dispatchable, - Extra: SignedExtension, + Extra: SignedExtension, Origin: From>, { type AccountId = AccountId; - type Call = Call; fn sender(&self) -> Option<&Self::AccountId> { @@ -61,9 +60,9 @@ where len: usize, ) -> TransactionValidity { if let Some((ref id, ref extra)) = self.signed { - Extra::validate(extra, id, info, len).into() + Extra::validate(extra, id, &self.function, info, len).into() } else { - match Extra::validate_unsigned(info, len) { + match Extra::validate_unsigned(&self.function, info, len) { Ok(extra) => match U::validate_unsigned(&self.function) { TransactionValidity::Valid(v) => TransactionValidity::Valid(v.combine_with(extra)), @@ -79,10 +78,10 @@ where len: usize, ) -> Result { let maybe_who = if let Some((id, extra)) = self.signed { - Extra::pre_dispatch(extra, &id, info, len)?; + Extra::pre_dispatch(extra, &id, &self.function, info, len)?; Some(id) } else { - Extra::pre_dispatch_unsigned(info, len)?; + Extra::pre_dispatch_unsigned(&self.function, info, len)?; None }; Ok(self.function.dispatch(Origin::from(maybe_who))) diff --git a/core/sr-primitives/src/generic/unchecked_extrinsic.rs b/core/sr-primitives/src/generic/unchecked_extrinsic.rs index d87c9e045f315..17157c4a19b0e 100644 --- a/core/sr-primitives/src/generic/unchecked_extrinsic.rs +++ b/core/sr-primitives/src/generic/unchecked_extrinsic.rs @@ -234,6 +234,7 @@ mod tests { struct TestExtra; impl SignedExtension for TestExtra { type AccountId = u64; + type Call = (); type AdditionalSigned = (); fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) } } diff --git a/core/sr-primitives/src/lib.rs b/core/sr-primitives/src/lib.rs index 1c9def98c1148..3c4c69f084ef6 100644 --- a/core/sr-primitives/src/lib.rs +++ b/core/sr-primitives/src/lib.rs @@ -31,10 +31,13 @@ pub use rstd; #[doc(hidden)] pub use paste; +#[doc(hidden)] +pub use app_crypto; + #[cfg(feature = "std")] pub use runtime_io::{StorageOverlay, ChildrenStorageOverlay}; -use rstd::{prelude::*, ops, convert::TryInto}; +use rstd::{prelude::*, ops, convert::{TryInto, TryFrom}}; use primitives::{crypto, ed25519, sr25519, hash::{H256, H512}}; use codec::{Encode, Decode}; @@ -52,7 +55,8 @@ pub mod transaction_validity; pub use generic::{DigestItem, Digest}; /// Re-export this since it's part of the API of this crate. -pub use primitives::crypto::{key_types, KeyTypeId}; +pub use primitives::crypto::{key_types, KeyTypeId, CryptoType}; +pub use app_crypto::AppKey; /// A message indicating an invalid signature in extrinsic. pub const BAD_SIGNATURE: &str = "bad signature in extrinsic"; @@ -660,8 +664,13 @@ pub struct AnySignature(H512); impl Verify for AnySignature { type Signer = sr25519::Public; fn verify>(&self, mut msg: L, signer: &sr25519::Public) -> bool { - runtime_io::sr25519_verify(self.0.as_fixed_bytes(), msg.get(), &signer.0) || - runtime_io::ed25519_verify(self.0.as_fixed_bytes(), msg.get(), &signer.0) + sr25519::Signature::try_from(self.0.as_fixed_bytes().as_ref()) + .map(|s| runtime_io::sr25519_verify(&s, msg.get(), &signer)) + .unwrap_or(false) + || ed25519::Signature::try_from(self.0.as_fixed_bytes().as_ref()) + .and_then(|s| ed25519::Public::try_from(signer.0.as_ref()).map(|p| (s, p))) + .map(|(s, p)| runtime_io::ed25519_verify(&s, msg.get(), &p)) + .unwrap_or(false) } } diff --git a/core/sr-primitives/src/testing.rs b/core/sr-primitives/src/testing.rs index 3d55e9c4b92ff..c91c8366c1123 100644 --- a/core/sr-primitives/src/testing.rs +++ b/core/sr-primitives/src/testing.rs @@ -20,46 +20,84 @@ use serde::{Serialize, Serializer, Deserialize, de::Error as DeError, Deserializ use std::{fmt::Debug, ops::Deref, fmt}; use crate::codec::{Codec, Encode, Decode}; use crate::traits::{ - self, Checkable, Applyable, BlakeTwo256, OpaqueKeys, TypedKey, DispatchError, DispatchResult, + self, Checkable, Applyable, BlakeTwo256, OpaqueKeys, DispatchError, DispatchResult, ValidateUnsigned, SignedExtension, Dispatchable, }; use crate::{generic, KeyTypeId}; use crate::weights::{GetDispatchInfo, DispatchInfo}; pub use primitives::H256; -use primitives::U256; -use primitives::ed25519::{Public as AuthorityId}; +use primitives::{crypto::{CryptoType, Dummy, key_types, Public}, U256}; use crate::transaction_validity::TransactionValidity; /// Authority Id -#[derive(Default, PartialEq, Eq, Clone, Encode, Decode, Debug)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(Default, PartialEq, Eq, Clone, Encode, Decode, Debug, Hash, Serialize, Deserialize)] pub struct UintAuthorityId(pub u64); -impl Into for UintAuthorityId { - fn into(self) -> AuthorityId { + +impl UintAuthorityId { + /// Convert this authority id into a public key. + pub fn to_public_key(&self) -> T { let bytes: [u8; 32] = U256::from(self.0).into(); - AuthorityId(bytes) + T::from_slice(&bytes) } } -/// The key-type of the `UintAuthorityId` -pub const UINT_DUMMY_KEY: KeyTypeId = 0xdeadbeef; - -impl TypedKey for UintAuthorityId { - const KEY_TYPE: KeyTypeId = UINT_DUMMY_KEY; +impl CryptoType for UintAuthorityId { + type Pair = Dummy; } impl AsRef<[u8]> for UintAuthorityId { fn as_ref(&self) -> &[u8] { - let ptr = self.0 as *const _; - // It's safe to do this here since `UintAuthorityId` is `u64`. - unsafe { std::slice::from_raw_parts(ptr, 8) } + unsafe { + std::slice::from_raw_parts(&self.0 as *const u64 as *const _, std::mem::size_of::()) + } + } +} + +impl app_crypto::RuntimeAppPublic for UintAuthorityId { + type Signature = u64; + + fn all() -> Vec { + unimplemented!("`all()` not available for `UintAuthorityId`.") + } + + #[cfg(feature = "std")] + fn generate_pair(_: Option<&str>) -> Self { + use rand::RngCore; + UintAuthorityId(rand::thread_rng().next_u64()) + } + + #[cfg(not(feature = "std"))] + fn generate_pair(_: Option<&str>) -> Self { + unimplemented!("`generate_pair` not implemented for `UIntAuthorityId` on `no_std`.") + } + + fn sign>(&self, msg: &M) -> Option { + let mut signature = [0u8; 8]; + msg.as_ref().iter() + .chain(rstd::iter::repeat(&42u8)) + .take(8) + .enumerate() + .for_each(|(i, v)| { signature[i] = *v; }); + + Some(u64::from_le_bytes(signature)) + } + + fn verify>(&self, msg: &M, signature: &Self::Signature) -> bool { + let mut msg_signature = [0u8; 8]; + msg.as_ref().iter() + .chain(rstd::iter::repeat(&42)) + .take(8) + .enumerate() + .for_each(|(i, v)| { msg_signature[i] = *v; }); + + u64::from_le_bytes(msg_signature) == *signature } } impl OpaqueKeys for UintAuthorityId { type KeyTypeIds = std::iter::Cloned>; - fn key_ids() -> Self::KeyTypeIds { [UINT_DUMMY_KEY].iter().cloned() } + fn key_ids() -> Self::KeyTypeIds { [key_types::DUMMY].iter().cloned() } // Unsafe, i know, but it's test code and it's just there because it's really convenient to // keep `UintAuthorityId` as a u64 under the hood. fn get_raw(&self, _: KeyTypeId) -> &[u8] { @@ -155,8 +193,7 @@ impl traits::Extrinsic for ExtrinsicWrapper { } } -impl serde::Serialize for ExtrinsicWrapper -{ +impl serde::Serialize for ExtrinsicWrapper { fn serialize(&self, seq: S) -> Result where S: ::serde::Serializer { self.using_encoded(|bytes| seq.serialize_bytes(bytes)) } @@ -249,13 +286,13 @@ impl traits::Extrinsic for TestXt impl Applyable for TestXt where Call: 'static + Sized + Send + Sync + Clone + Eq + Codec + Debug + Dispatchable, - Extra: SignedExtension, + Extra: SignedExtension, Origin: From> { type AccountId = u64; type Call = Call; - fn sender(&self) -> Option<&u64> { self.0.as_ref().map(|x| &x.0) } + fn sender(&self) -> Option<&Self::AccountId> { self.0.as_ref().map(|x| &x.0) } /// Checks to see if this is a valid *transaction*. It returns information on it if so. fn validate>(&self, @@ -272,10 +309,10 @@ impl Applyable for TestXt where len: usize, ) -> Result { let maybe_who = if let Some((who, extra)) = self.0 { - Extra::pre_dispatch(extra, &who, info, len)?; + Extra::pre_dispatch(extra, &who, &self.1, info, len)?; Some(who) } else { - Extra::pre_dispatch_unsigned(info, len)?; + Extra::pre_dispatch_unsigned(&self.1, info, len)?; None }; Ok(self.1.dispatch(maybe_who.into())) diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index ce67a888bc467..442aa9b0ac89a 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -26,7 +26,6 @@ use crate::codec::{Codec, Encode, Decode, HasCompact}; use crate::transaction_validity::{ValidTransaction, TransactionValidity}; use crate::generic::{Digest, DigestItem}; use crate::weights::DispatchInfo; -pub use primitives::crypto::TypedKey; pub use integer_sqrt::IntegerSquareRoot; pub use num_traits::{ Zero, One, Bounded, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, @@ -60,14 +59,14 @@ pub trait Verify { impl Verify for primitives::ed25519::Signature { type Signer = primitives::ed25519::Public; fn verify>(&self, mut msg: L, signer: &Self::Signer) -> bool { - runtime_io::ed25519_verify(self.as_ref(), msg.get(), signer) + runtime_io::ed25519_verify(self, msg.get(), signer) } } impl Verify for primitives::sr25519::Signature { type Signer = primitives::sr25519::Public; fn verify>(&self, mut msg: L, signer: &Self::Signer) -> bool { - runtime_io::sr25519_verify(self.as_ref(), msg.get(), signer) + runtime_io::sr25519_verify(self, msg.get(), signer) } } @@ -745,7 +744,7 @@ pub enum DispatchError { Payment, /// General error to do with the exhaustion of block resources. - Resource, + Exhausted, /// General error to do with the permissions of the sender. NoPermission, @@ -761,16 +760,13 @@ pub enum DispatchError { /// General error to do with the transaction's proofs (e.g. signature). BadProof, - -/* /// General error to do with actually executing the dispatched logic. - User(&'static str),*/ } impl From for i8 { fn from(e: DispatchError) -> i8 { match e { DispatchError::Payment => -64, - DispatchError::Resource => -65, + DispatchError::Exhausted => -65, DispatchError::NoPermission => -66, DispatchError::BadState => -67, DispatchError::Stale => -68, @@ -805,6 +801,9 @@ pub trait SignedExtension: /// The type which encodes the sender identity. type AccountId; + /// The type which encodes the call to be dispatched. + type Call; + /// Any additional data that will go into the signed payload. This may be created dynamically /// from the transaction using the `additional_signed` function. type AdditionalSigned: Encode; @@ -813,35 +812,45 @@ pub trait SignedExtension: /// also perform any pre-signature-verification checks and return an error if needed. fn additional_signed(&self) -> Result; - /// Validate a signed transaction for the transaction queue. + /// Validate a signed transaction for the transaction queue. fn validate( &self, _who: &Self::AccountId, + _call: &Self::Call, _info: DispatchInfo, _len: usize, - ) -> Result { Ok(Default::default()) } + ) -> Result { + Ok(Default::default()) + } /// Do any pre-flight stuff for a signed transaction. fn pre_dispatch( self, who: &Self::AccountId, + call: &Self::Call, info: DispatchInfo, len: usize, - ) -> Result<(), DispatchError> { self.validate(who, info, len).map(|_| ()) } + ) -> Result<(), DispatchError> { + self.validate(who, call, info, len).map(|_| ()) + } /// Validate an unsigned transaction for the transaction queue. Normally the default /// implementation is fine since `ValidateUnsigned` is a better way of recognising and /// validating unsigned transactions. fn validate_unsigned( + _call: &Self::Call, _info: DispatchInfo, _len: usize, ) -> Result { Ok(Default::default()) } /// Do any pre-flight stuff for a unsigned transaction. fn pre_dispatch_unsigned( + call: &Self::Call, info: DispatchInfo, len: usize, - ) -> Result<(), DispatchError> { Self::validate_unsigned(info, len).map(|_| ()) } + ) -> Result<(), DispatchError> { + Self::validate_unsigned(call, info, len).map(|_| ()) + } } macro_rules! tuple_impl_indexed { @@ -851,9 +860,11 @@ macro_rules! tuple_impl_indexed { ([$($direct:ident)+] ; [$($index:tt,)+]) => { impl< AccountId, - $($direct: SignedExtension),+ + Call, + $($direct: SignedExtension),+ > SignedExtension for ($($direct),+,) { type AccountId = AccountId; + type Call = Call; type AdditionalSigned = ($($direct::AdditionalSigned,)+); fn additional_signed(&self) -> Result { Ok(( $(self.$index.additional_signed()?,)+ )) @@ -861,33 +872,37 @@ macro_rules! tuple_impl_indexed { fn validate( &self, who: &Self::AccountId, + call: &Self::Call, info: DispatchInfo, len: usize, ) -> Result { - let aggregator = vec![$(<$direct as SignedExtension>::validate(&self.$index, who, info, len)?),+]; + let aggregator = vec![$(<$direct as SignedExtension>::validate(&self.$index, who, call, info, len)?),+]; Ok(aggregator.into_iter().fold(ValidTransaction::default(), |acc, a| acc.combine_with(a))) } fn pre_dispatch( self, who: &Self::AccountId, + call: &Self::Call, info: DispatchInfo, len: usize, ) -> Result<(), DispatchError> { - $(self.$index.pre_dispatch(who, info, len)?;)+ + $(self.$index.pre_dispatch(who, call, info, len)?;)+ Ok(()) } fn validate_unsigned( + call: &Self::Call, info: DispatchInfo, len: usize, ) -> Result { - let aggregator = vec![$($direct::validate_unsigned(info, len)?),+]; + let aggregator = vec![$($direct::validate_unsigned(call, info, len)?),+]; Ok(aggregator.into_iter().fold(ValidTransaction::default(), |acc, a| acc.combine_with(a))) } fn pre_dispatch_unsigned( + call: &Self::Call, info: DispatchInfo, len: usize, ) -> Result<(), DispatchError> { - $($direct::pre_dispatch_unsigned(info, len)?;)+ + $($direct::pre_dispatch_unsigned(call, info, len)?;)+ Ok(()) } } @@ -910,11 +925,12 @@ macro_rules! tuple_impl_indexed { #[allow(non_snake_case)] tuple_impl_indexed!(A, B, C, D, E, F, G, H, I, J, ; 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,); -/// Only for base bone testing when you don't care about signed extensions at all.\ +/// Only for bare bone testing when you don't care about signed extensions at all. #[cfg(feature = "std")] impl SignedExtension for () { type AccountId = u64; type AdditionalSigned = (); + type Call = (); fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) } } @@ -1208,14 +1224,14 @@ macro_rules! count { /// just the bytes of the key. /// /// ```rust -/// use sr_primitives::{impl_opaque_keys, key_types, KeyTypeId}; +/// use sr_primitives::{impl_opaque_keys, key_types, KeyTypeId, app_crypto::{sr25519, ed25519}}; /// /// impl_opaque_keys! { /// pub struct Keys { /// #[id(key_types::ED25519)] -/// pub ed25519: [u8; 32], +/// pub ed25519: ed25519::AppPublic, /// #[id(key_types::SR25519)] -/// pub sr25519: [u8; 32], +/// pub sr25519: sr25519::AppPublic, /// } /// } /// ``` @@ -1237,22 +1253,34 @@ macro_rules! impl_opaque_keys { )* } + impl $name { + /// Generate a set of keys with optionally using the given seed. + /// + /// The generated key pairs are stored in the keystore. + /// + /// Returns the concatenated SCALE encoded public keys. + pub fn generate(seed: Option<&str>) -> $crate::rstd::vec::Vec { + let keys = Self{ + $( + $field: <$type as $crate::app_crypto::RuntimeAppPublic>::generate_pair(seed), + )* + }; + $crate::codec::Encode::encode(&keys) + } + } + impl $crate::traits::OpaqueKeys for $name { type KeyTypeIds = $crate::rstd::iter::Cloned< $crate::rstd::slice::Iter<'static, $crate::KeyTypeId> >; fn key_ids() -> Self::KeyTypeIds { - [ - $($key_id),* - ].iter().cloned() + [ $($key_id),* ].iter().cloned() } fn get_raw(&self, i: $crate::KeyTypeId) -> &[u8] { match i { - $( - i if i == $key_id => self.$field.as_ref(), - )* + $( i if i == $key_id => self.$field.as_ref(), )* _ => &[], } } diff --git a/core/state-db/Cargo.toml b/core/state-db/Cargo.toml index 55d66ea5f8d98..332751c927f5d 100644 --- a/core/state-db/Cargo.toml +++ b/core/state-db/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -parking_lot = "0.8.0" +parking_lot = "0.9.0" log = "0.4" primitives = { package = "substrate-primitives", path = "../../core/primitives" } codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } diff --git a/core/state-machine/Cargo.toml b/core/state-machine/Cargo.toml index bb00cfcbb7aa4..bf5b96e43675a 100644 --- a/core/state-machine/Cargo.toml +++ b/core/state-machine/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [dependencies] log = "0.4" -parking_lot = "0.8.0" +parking_lot = "0.9.0" hash-db = "0.15.0" trie-db = "0.15.0" trie-root = "0.15.0" diff --git a/core/state-machine/src/basic.rs b/core/state-machine/src/basic.rs index 53ab731671314..85f5fa28b6369 100644 --- a/core/state-machine/src/basic.rs +++ b/core/state-machine/src/basic.rs @@ -168,7 +168,12 @@ impl Externalities for BasicExternalities where H::Out: Ord { } fn offchain(&mut self) -> Option<&mut dyn offchain::Externalities> { - warn!("Call to non-existent out offchain externalities set."); + warn!("Call to non-existent offchain externalities set."); + None + } + + fn keystore(&self) -> Option { + warn!("Call to non-existent keystore."); None } } diff --git a/core/state-machine/src/ext.rs b/core/state-machine/src/ext.rs index 8c54806dd9112..f7ff649925ea2 100644 --- a/core/state-machine/src/ext.rs +++ b/core/state-machine/src/ext.rs @@ -22,8 +22,7 @@ use crate::backend::Backend; use crate::changes_trie::{Storage as ChangesTrieStorage, build_changes_trie}; use crate::{Externalities, OverlayedChanges, ChildStorageKey}; use hash_db::Hasher; -use primitives::offchain; -use primitives::storage::well_known_keys::is_child_storage_key; +use primitives::{offchain, storage::well_known_keys::is_child_storage_key, traits::BareCryptoStorePtr}; use trie::{MemoryDB, default_child_trie_root}; use trie::trie_types::Layout; @@ -84,6 +83,8 @@ where /// /// If None, some methods from the trait might not be supported. offchain_externalities: Option<&'a mut O>, + /// The keystore that manages the keys of the node. + keystore: Option, /// Dummy usage of N arg. _phantom: ::std::marker::PhantomData, } @@ -103,6 +104,7 @@ where backend: &'a B, changes_trie_storage: Option<&'a T>, offchain_externalities: Option<&'a mut O>, + keystore: Option, ) -> Self { Ext { overlay, @@ -111,6 +113,7 @@ where changes_trie_storage, changes_trie_transaction: None, offchain_externalities, + keystore, _phantom: Default::default(), } } @@ -333,6 +336,10 @@ where fn offchain(&mut self) -> Option<&mut dyn offchain::Externalities> { self.offchain_externalities.as_mut().map(|x| &mut **x as _) } + + fn keystore(&self) -> Option { + self.keystore.clone() + } } #[cfg(test)] @@ -375,7 +382,7 @@ mod tests { fn storage_changes_root_is_none_when_storage_is_not_provided() { let mut overlay = prepare_overlay_with_changes(); let backend = TestBackend::default(); - let mut ext = TestExt::new(&mut overlay, &backend, None, None); + let mut ext = TestExt::new(&mut overlay, &backend, None, None, None); assert_eq!(ext.storage_changes_root(Default::default()).unwrap(), None); } @@ -385,7 +392,7 @@ mod tests { overlay.changes_trie_config = None; let storage = TestChangesTrieStorage::with_blocks(vec![(100, Default::default())]); let backend = TestBackend::default(); - let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None); + let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None, None); assert_eq!(ext.storage_changes_root(Default::default()).unwrap(), None); } @@ -394,11 +401,11 @@ mod tests { let mut overlay = prepare_overlay_with_changes(); let storage = TestChangesTrieStorage::with_blocks(vec![(99, Default::default())]); let backend = TestBackend::default(); - let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None); - let root = hex!("bb0c2ef6e1d36d5490f9766cfcc7dfe2a6ca804504c3bb206053890d6dd02376").into(); - - assert_eq!(ext.storage_changes_root(Default::default()).unwrap(), - Some(root)); + let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None, None); + assert_eq!( + ext.storage_changes_root(Default::default()).unwrap(), + Some(hex!("bb0c2ef6e1d36d5490f9766cfcc7dfe2a6ca804504c3bb206053890d6dd02376").into()), + ); } #[test] @@ -407,10 +414,10 @@ mod tests { overlay.prospective.top.get_mut(&vec![1]).unwrap().value = None; let storage = TestChangesTrieStorage::with_blocks(vec![(99, Default::default())]); let backend = TestBackend::default(); - let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None); - let root = hex!("96f5aae4690e7302737b6f9b7f8567d5bbb9eac1c315f80101235a92d9ec27f4").into(); - - assert_eq!(ext.storage_changes_root(Default::default()).unwrap(), - Some(root)); + let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None, None); + assert_eq!( + ext.storage_changes_root(Default::default()).unwrap(), + Some(hex!("96f5aae4690e7302737b6f9b7f8567d5bbb9eac1c315f80101235a92d9ec27f4").into()), + ); } } diff --git a/core/state-machine/src/lib.rs b/core/state-machine/src/lib.rs index b98228e9b240c..6279966cae135 100644 --- a/core/state-machine/src/lib.rs +++ b/core/state-machine/src/lib.rs @@ -25,6 +25,7 @@ use hash_db::Hasher; use codec::{Decode, Encode}; use primitives::{ storage::well_known_keys, NativeOrEncoded, NeverNativeValue, offchain, + traits::BareCryptoStorePtr, }; pub mod backend; @@ -61,6 +62,7 @@ pub use proving_backend::{ pub use trie_backend_essence::{TrieBackendStorage, Storage}; pub use trie_backend::TrieBackend; + /// A wrapper around a child storage key. /// /// This wrapper ensures that the child storage key is correct and properly used. It is @@ -224,6 +226,9 @@ pub trait Externalities { /// Returns offchain externalities extension if present. fn offchain(&mut self) -> Option<&mut dyn offchain::Externalities>; + + /// Returns the keystore. + fn keystore(&self) -> Option; } /// An implementation of offchain extensions that should never be triggered. @@ -247,53 +252,6 @@ impl offchain::Externalities for NeverOffchainExt { unreachable!() } - fn pubkey( - &self, - _key: offchain::CryptoKey, - ) -> Result, ()> { - unreachable!() - } - - fn new_crypto_key( - &mut self, - _crypto: offchain::CryptoKind, - ) -> Result { - unreachable!() - } - - fn encrypt( - &mut self, - _key: offchain::CryptoKey, - _data: &[u8], - ) -> Result, ()> { - unreachable!() - } - - fn decrypt( - &mut self, - _key: offchain::CryptoKey, - _data: &[u8], - ) -> Result, ()> { - unreachable!() - } - - fn sign( - &mut self, - _key: offchain::CryptoKey, - _data: &[u8], - ) -> Result, ()> { - unreachable!() - } - - fn verify( - &mut self, - _key: offchain::CryptoKey, - _msg: &[u8], - _signature: &[u8], - ) -> Result { - unreachable!() - } - fn timestamp(&mut self) -> offchain::Timestamp { unreachable!() } @@ -481,6 +439,7 @@ pub fn new<'a, H, N, B, T, O, Exec>( exec: &'a Exec, method: &'a str, call_data: &'a [u8], + keystore: Option, ) -> StateMachine<'a, H, N, B, T, O, Exec> { StateMachine { backend, @@ -490,6 +449,7 @@ pub fn new<'a, H, N, B, T, O, Exec>( exec, method, call_data, + keystore, _hasher: PhantomData, } } @@ -503,6 +463,7 @@ pub struct StateMachine<'a, H, N, B, T, O, Exec> { exec: &'a Exec, method: &'a str, call_data: &'a [u8], + keystore: Option, _hasher: PhantomData<(H, N)>, } @@ -560,6 +521,7 @@ impl<'a, H, N, B, T, O, Exec> StateMachine<'a, H, N, B, T, O, Exec> where self.backend, self.changes_trie_storage, self.offchain_ext.as_mut().map(|x| &mut **x), + self.keystore.clone(), ); let (result, was_native) = self.exec.call( &mut externalities, @@ -707,6 +669,7 @@ pub fn prove_execution( exec: &Exec, method: &str, call_data: &[u8], + keystore: Option, ) -> Result<(Vec, Vec>), Box> where B: Backend, @@ -716,7 +679,7 @@ where { let trie_backend = backend.as_trie_backend() .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; - prove_execution_on_trie_backend(trie_backend, overlay, exec, method, call_data) + prove_execution_on_trie_backend(trie_backend, overlay, exec, method, call_data, keystore) } /// Prove execution using the given trie backend, overlayed changes, and call executor. @@ -734,6 +697,7 @@ pub fn prove_execution_on_trie_backend( exec: &Exec, method: &str, call_data: &[u8], + keystore: Option, ) -> Result<(Vec, Vec>), Box> where S: trie_backend_essence::TrieBackendStorage, @@ -750,6 +714,7 @@ where exec, method, call_data, + keystore, _hasher: PhantomData, }; let (result, _, _) = sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( @@ -769,6 +734,7 @@ pub fn execution_proof_check( exec: &Exec, method: &str, call_data: &[u8], + keystore: Option, ) -> Result, Box> where H: Hasher, @@ -776,7 +742,7 @@ where H::Out: Ord + 'static, { let trie_backend = create_proof_check_backend::(root.into(), proof)?; - execution_proof_check_on_trie_backend(&trie_backend, overlay, exec, method, call_data) + execution_proof_check_on_trie_backend(&trie_backend, overlay, exec, method, call_data, keystore) } /// Check execution proof on proving backend, generated by `prove_execution` call. @@ -786,6 +752,7 @@ pub fn execution_proof_check_on_trie_backend( exec: &Exec, method: &str, call_data: &[u8], + keystore: Option, ) -> Result, Box> where H: Hasher, @@ -800,6 +767,7 @@ where exec, method, call_data, + keystore, _hasher: PhantomData, }; sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( @@ -1050,6 +1018,7 @@ mod tests { }, "test", &[], + None, ).execute( ExecutionStrategy::NativeWhenPossible ).unwrap().0, vec![66]); @@ -1071,6 +1040,7 @@ mod tests { }, "test", &[], + None, ).execute( ExecutionStrategy::NativeElseWasm ).unwrap().0, vec![66]); @@ -1092,6 +1062,7 @@ mod tests { }, "test", &[], + None, ).execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( ExecutionManager::Both(|we, _ne| { consensus_failed = true; @@ -1114,13 +1085,26 @@ mod tests { // fetch execution proof from 'remote' full node let remote_backend = trie_backend::tests::test_trie(); - let remote_root = remote_backend.storage_root(::std::iter::empty()).0; - let (remote_result, remote_proof) = prove_execution(remote_backend, - &mut Default::default(), &executor, "test", &[]).unwrap(); + let remote_root = remote_backend.storage_root(std::iter::empty()).0; + let (remote_result, remote_proof) = prove_execution( + remote_backend, + &mut Default::default(), + &executor, + "test", + &[], + None, + ).unwrap(); // check proof locally - let local_result = execution_proof_check::(remote_root, remote_proof, - &mut Default::default(), &executor, "test", &[]).unwrap(); + let local_result = execution_proof_check::( + remote_root, + remote_proof, + &mut Default::default(), + &executor, + "test", + &[], + None, + ).unwrap(); // check that both results are correct assert_eq!(remote_result, vec![66]); @@ -1151,7 +1135,13 @@ mod tests { { let changes_trie_storage = InMemoryChangesTrieStorage::::new(); - let mut ext = Ext::new(&mut overlay, backend, Some(&changes_trie_storage), NeverOffchainExt::new()); + let mut ext = Ext::new( + &mut overlay, + backend, + Some(&changes_trie_storage), + NeverOffchainExt::new(), + None, + ); ext.clear_prefix(b"ab"); } overlay.commit_prospective(); @@ -1180,7 +1170,8 @@ mod tests { &mut overlay, backend, Some(&changes_trie_storage), - NeverOffchainExt::new() + NeverOffchainExt::new(), + None, ); ext.set_child_storage( @@ -1252,41 +1243,47 @@ mod tests { #[test] fn cannot_change_changes_trie_config() { - assert!(new( - &trie_backend::tests::test_trie(), - Some(&InMemoryChangesTrieStorage::::new()), - NeverOffchainExt::new(), - &mut Default::default(), - &DummyCodeExecutor { - change_changes_trie_config: true, - native_available: false, - native_succeeds: true, - fallback_succeeds: true, - }, - "test", - &[], - ).execute( - ExecutionStrategy::NativeWhenPossible - ).is_err()); + assert!( + new( + &trie_backend::tests::test_trie(), + Some(&InMemoryChangesTrieStorage::::new()), + NeverOffchainExt::new(), + &mut Default::default(), + &DummyCodeExecutor { + change_changes_trie_config: true, + native_available: false, + native_succeeds: true, + fallback_succeeds: true, + }, + "test", + &[], + None, + ) + .execute(ExecutionStrategy::NativeWhenPossible) + .is_err() + ); } #[test] fn cannot_change_changes_trie_config_with_native_else_wasm() { - assert!(new( - &trie_backend::tests::test_trie(), - Some(&InMemoryChangesTrieStorage::::new()), - NeverOffchainExt::new(), - &mut Default::default(), - &DummyCodeExecutor { - change_changes_trie_config: true, - native_available: false, - native_succeeds: true, - fallback_succeeds: true, - }, - "test", - &[], - ).execute( - ExecutionStrategy::NativeElseWasm - ).is_err()); + assert!( + new( + &trie_backend::tests::test_trie(), + Some(&InMemoryChangesTrieStorage::::new()), + NeverOffchainExt::new(), + &mut Default::default(), + &DummyCodeExecutor { + change_changes_trie_config: true, + native_available: false, + native_succeeds: true, + fallback_succeeds: true, + }, + "test", + &[], + None, + ) + .execute(ExecutionStrategy::NativeElseWasm) + .is_err() + ); } } diff --git a/core/state-machine/src/overlayed_changes.rs b/core/state-machine/src/overlayed_changes.rs index 8c4160760b526..edc03baa393ef 100644 --- a/core/state-machine/src/overlayed_changes.rs +++ b/core/state-machine/src/overlayed_changes.rs @@ -371,6 +371,7 @@ mod tests { &backend, Some(&changes_trie_storage), crate::NeverOffchainExt::new(), + None, ); const ROOT: [u8; 32] = hex!("39245109cef3758c2eed2ccba8d9b370a917850af3824bc8348d505df2c298fa"); diff --git a/core/state-machine/src/testing.rs b/core/state-machine/src/testing.rs index 89290a59e2f8a..0722eda456334 100644 --- a/core/state-machine/src/testing.rs +++ b/core/state-machine/src/testing.rs @@ -25,8 +25,9 @@ use crate::changes_trie::{ build_changes_trie, InMemoryStorage as ChangesTrieInMemoryStorage, BlockNumber as ChangesTrieBlockNumber, }; -use primitives::offchain; -use primitives::storage::well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES}; +use primitives::{ + storage::well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES}, traits::BareCryptoStorePtr, offchain +}; use codec::Encode; use super::{ChildStorageKey, Externalities, OverlayedChanges}; @@ -40,6 +41,7 @@ pub struct TestExternalities { backend: InMemory, changes_trie_storage: ChangesTrieInMemoryStorage, offchain: Option>, + keystore: Option, } impl TestExternalities { @@ -84,6 +86,7 @@ impl TestExternalities { changes_trie_storage: ChangesTrieInMemoryStorage::new(), backend: backend.into(), offchain: None, + keystore: None, } } @@ -263,6 +266,10 @@ impl Externalities for TestExternalities .as_mut() .map(|x| &mut **x as _) } + + fn keystore(&self) -> Option { + self.keystore.clone() + } } #[cfg(test)] diff --git a/core/telemetry/Cargo.toml b/core/telemetry/Cargo.toml index 540305435f706..8647462739861 100644 --- a/core/telemetry/Cargo.toml +++ b/core/telemetry/Cargo.toml @@ -7,7 +7,7 @@ edition = "2018" [dependencies] bytes = "0.4" -parking_lot = "0.8.0" +parking_lot = "0.9.0" futures01 = { package = "futures", version = "0.1" } futures-preview = { version = "0.3.0-alpha.17", features = ["compat"] } futures-timer = "0.2.1" diff --git a/core/test-client/src/lib.rs b/core/test-client/src/lib.rs index 175996a43260c..d0d4a54b0f4ae 100644 --- a/core/test-client/src/lib.rs +++ b/core/test-client/src/lib.rs @@ -30,7 +30,7 @@ pub use keyring::{ ed25519::Keyring as Ed25519Keyring, sr25519::Keyring as Sr25519Keyring, }; -pub use primitives::Blake2Hasher; +pub use primitives::{Blake2Hasher, traits::BareCryptoStorePtr}; pub use sr_primitives::{StorageOverlay, ChildrenStorageOverlay}; pub use state_machine::ExecutionStrategy; @@ -73,6 +73,7 @@ pub struct TestClientBuilder { child_storage_extension: HashMap, Vec<(Vec, Vec)>>, backend: Arc, _executor: std::marker::PhantomData, + keystore: Option, } impl Default for TestClientBuilder< @@ -100,11 +101,7 @@ impl TestClientBuilder< } } -impl TestClientBuilder< - Executor, - Backend, - G, -> { +impl TestClientBuilder { /// Create a new instance of the test client builder. pub fn with_backend(backend: Arc) -> Self { TestClientBuilder { @@ -113,9 +110,16 @@ impl TestClientBuilder< child_storage_extension: Default::default(), genesis_init: Default::default(), _executor: Default::default(), + keystore: None, } } + /// Set the keystore that should be used by the externalities. + pub fn set_keystore(mut self, keystore: BareCryptoStorePtr) -> Self { + self.keystore = Some(keystore); + self + } + /// Alter the genesis storage parameters. pub fn genesis_init_mut(&mut self) -> &mut G { &mut self.genesis_init @@ -184,7 +188,7 @@ impl TestClientBuilder< self.backend.clone(), executor, storage, - self.execution_strategies + self.execution_strategies, ).expect("Creates new client"); let longest_chain = client::LongestChain::new(self.backend); @@ -200,7 +204,7 @@ impl TestClientBuilder< > { /// Build the test client with the given native executor. pub fn build_with_native_executor( - self, + mut self, executor: I, ) -> ( client::Client< @@ -217,7 +221,7 @@ impl TestClientBuilder< Block: BlockT::Out>, { let executor = executor.into().unwrap_or_else(|| executor::NativeExecutor::new(None)); - let executor = LocalCallExecutor::new(self.backend.clone(), executor); + let executor = LocalCallExecutor::new(self.backend.clone(), executor, self.keystore.take()); self.build_with_executor(executor) } diff --git a/core/test-runtime/Cargo.toml b/core/test-runtime/Cargo.toml index 03c95a5c27644..98d110a574c6c 100644 --- a/core/test-runtime/Cargo.toml +++ b/core/test-runtime/Cargo.toml @@ -12,6 +12,7 @@ codec = { package = "parity-scale-codec", version = "1.0.0", default-features = keyring = { package = "substrate-keyring", path = "../keyring", optional = true } substrate-client = { path = "../client", default-features = false } primitives = { package = "substrate-primitives", path = "../primitives", default-features = false } +app-crypto = { package = "substrate-application-crypto", path = "../application-crypto", default-features = false } inherents = { package = "substrate-inherents", path = "../inherents", default-features = false } aura-primitives = { package = "substrate-consensus-aura-primitives", path = "../consensus/aura/primitives", default-features = false } babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../consensus/babe/primitives", default-features = false } @@ -67,4 +68,5 @@ std = [ "srml-babe/std", "srml-timestamp/std", "srml-system/std", + "app-crypto/std", ] diff --git a/core/test-runtime/client/src/lib.rs b/core/test-runtime/client/src/lib.rs index 36a2fd3e86d40..03f7bb3ddb7db 100644 --- a/core/test-runtime/client/src/lib.rs +++ b/core/test-runtime/client/src/lib.rs @@ -26,6 +26,7 @@ pub use block_builder_ext::BlockBuilderExt; pub use generic_test_client::*; pub use runtime; +use primitives::sr25519; use runtime::genesismap::{GenesisConfig, additional_storage_with_genesis}; use sr_primitives::traits::{Block as BlockT, Header as HeaderT, Hash as HashT}; @@ -183,9 +184,9 @@ fn genesis_config(support_changes_trie: bool, heap_pages_override: Option) GenesisConfig::new( support_changes_trie, vec![ - Sr25519Keyring::Alice.into(), - Sr25519Keyring::Bob.into(), - Sr25519Keyring::Charlie.into(), + sr25519::Public::from(Sr25519Keyring::Alice).into(), + sr25519::Public::from(Sr25519Keyring::Bob).into(), + sr25519::Public::from(Sr25519Keyring::Charlie).into(), ], vec![ AccountKeyring::Alice.into(), AccountKeyring::Bob.into(), @@ -210,9 +211,16 @@ pub fn new_light() -> client::Client Option { if let Extrinsic::IncludeData(_) = *self { @@ -149,8 +150,8 @@ impl ExtrinsicT for Extrinsic { } } - fn new_unsigned(_call: Self::Call) -> Option { - None + fn new_unsigned(call: Self::Call) -> Option { + Some(call) } } @@ -267,6 +268,14 @@ cfg_if! { /// Returns if no block was initialized. #[skip_initialize_block] fn without_initialize_block() -> bool; + /// Test that `ed25519` crypto works in the runtime. + /// + /// Returns the signature generated for the message `ed25519` and the public key. + fn test_ed25519_crypto() -> (ed25519::AppSignature, ed25519::AppPublic); + /// Test that `sr25519` crypto works in the runtime. + /// + /// Returns the signature generated for the message `sr25519`. + fn test_sr25519_crypto() -> (sr25519::AppSignature, sr25519::AppPublic); } } } else { @@ -300,6 +309,14 @@ cfg_if! { /// Returns if no block was initialized. #[skip_initialize_block] fn without_initialize_block() -> bool; + /// Test that `ed25519` crypto works in the runtime. + /// + /// Returns the signature generated for the message `ed25519` and the public key. + fn test_ed25519_crypto() -> (ed25519::AppSignature, ed25519::AppPublic); + /// Test that `sr25519` crypto works in the runtime. + /// + /// Returns the signature generated for the message `sr25519`. + fn test_sr25519_crypto() -> (sr25519::AppSignature, sr25519::AppPublic); } } } @@ -340,6 +357,7 @@ parameter_types! { impl srml_system::Trait for Runtime { type Origin = Origin; + type Call = Extrinsic; type Index = u64; type BlockNumber = u64; type Hash = H256; @@ -545,11 +563,24 @@ cfg_if! { fn take_block_number() -> Option { system::take_block_number() } + + fn test_ed25519_crypto() -> (ed25519::AppSignature, ed25519::AppPublic) { + test_ed25519_crypto() + } + + fn test_sr25519_crypto() -> (sr25519::AppSignature, sr25519::AppPublic) { + test_sr25519_crypto() + } } impl aura_primitives::AuraApi for Runtime { fn slot_duration() -> u64 { 1000 } - fn authorities() -> Vec { system::authorities() } + fn authorities() -> Vec { + system::authorities().into_iter().map(|a| { + let authority: sr25519::Public = a.into(); + AuraId::from(authority) + }).collect() + } } impl babe_primitives::BabeApi for Runtime { @@ -736,11 +767,24 @@ cfg_if! { fn take_block_number() -> Option { system::take_block_number() } + + fn test_ed25519_crypto() -> (ed25519::AppSignature, ed25519::AppPublic) { + test_ed25519_crypto() + } + + fn test_sr25519_crypto() -> (sr25519::AppSignature, sr25519::AppPublic) { + test_sr25519_crypto() + } } impl aura_primitives::AuraApi for Runtime { fn slot_duration() -> u64 { 1000 } - fn authorities() -> Vec { system::authorities() } + fn authorities() -> Vec { + system::authorities().into_iter().map(|a| { + let authority: sr25519::Public = a.into(); + AuraId::from(authority) + }).collect() + } } impl babe_primitives::BabeApi for Runtime { @@ -776,6 +820,36 @@ cfg_if! { } } +fn test_ed25519_crypto() -> (ed25519::AppSignature, ed25519::AppPublic) { + let public0 = ed25519::AppPublic::generate_pair(None); + let public1 = ed25519::AppPublic::generate_pair(None); + let public2 = ed25519::AppPublic::generate_pair(None); + + let all = ed25519::AppPublic::all(); + assert!(all.contains(&public0)); + assert!(all.contains(&public1)); + assert!(all.contains(&public2)); + + let signature = public0.sign(&"ed25519").expect("Generates a valid `ed25519` signature."); + assert!(public0.verify(&"ed25519", &signature)); + (signature, public0) +} + +fn test_sr25519_crypto() -> (sr25519::AppSignature, sr25519::AppPublic) { + let public0 = sr25519::AppPublic::generate_pair(None); + let public1 = sr25519::AppPublic::generate_pair(None); + let public2 = sr25519::AppPublic::generate_pair(None); + + let all = sr25519::AppPublic::all(); + assert!(all.contains(&public0)); + assert!(all.contains(&public1)); + assert!(all.contains(&public2)); + + let signature = public0.sign(&"sr25519").expect("Generates a valid `sr25519` signature."); + assert!(public0.verify(&"sr25519", &signature)); + (signature, public0) +} + #[cfg(test)] mod tests { use substrate_test_runtime_client::{ diff --git a/core/test-runtime/src/system.rs b/core/test-runtime/src/system.rs index c108672c83e5c..a1e407bd6e3c1 100644 --- a/core/test-runtime/src/system.rs +++ b/core/test-runtime/src/system.rs @@ -274,8 +274,7 @@ fn execute_transfer_backend(tx: &Transfer) -> ApplyResult { } fn execute_new_authorities_backend(new_authorities: &[AuthorityId]) -> ApplyResult { - let new_authorities: Vec = new_authorities.iter().cloned().collect(); - ::put(new_authorities); + NewAuthorities::put(new_authorities.to_vec()); Ok(ApplyOutcome::Success) } diff --git a/core/transaction-pool/Cargo.toml b/core/transaction-pool/Cargo.toml index c18a50778df64..747c39a16566a 100644 --- a/core/transaction-pool/Cargo.toml +++ b/core/transaction-pool/Cargo.toml @@ -9,7 +9,7 @@ derive_more = "0.14.0" futures = "0.1" log = "0.4" codec = { package = "parity-scale-codec", version = "1.0.0" } -parking_lot = "0.8.0" +parking_lot = "0.9.0" sr-primitives = { path = "../sr-primitives" } client = { package = "substrate-client", path = "../client" } primitives = { package = "substrate-primitives", path = "../primitives" } diff --git a/core/transaction-pool/graph/Cargo.toml b/core/transaction-pool/graph/Cargo.toml index 8010b07ac4aad..4dc7ce69cffe9 100644 --- a/core/transaction-pool/graph/Cargo.toml +++ b/core/transaction-pool/graph/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" derive_more = "0.14.0" futures = "0.1" log = "0.4" -parking_lot = "0.8.0" +parking_lot = "0.9.0" serde = { version = "1.0", features = ["derive"] } primitives = { package = "substrate-primitives", path = "../../primitives" } sr-primitives = { path = "../../sr-primitives" } diff --git a/node-template/Cargo.toml b/node-template/Cargo.toml index ea48ff7f68305..4112452b2722f 100644 --- a/node-template/Cargo.toml +++ b/node-template/Cargo.toml @@ -16,7 +16,7 @@ ctrlc = { version = "3.0", features = ["termination"] } log = "0.4" tokio = "0.1" exit-future = "0.1" -parking_lot = "0.8.0" +parking_lot = "0.9.0" codec = { package = "parity-scale-codec", version = "1.0.0" } trie-root = "0.15.0" sr-io = { path = "../core/sr-io" } @@ -28,6 +28,8 @@ inherents = { package = "substrate-inherents", path = "../core/inherents" } transaction-pool = { package = "substrate-transaction-pool", path = "../core/transaction-pool" } network = { package = "substrate-network", path = "../core/network" } consensus = { package = "substrate-consensus-aura", path = "../core/consensus/aura" } +aura-primitives = { package = "substrate-consensus-aura-primitives", path = "../core/consensus/aura/primitives" } +grandpa-primitives = { package = "substrate-finality-grandpa-primitives", path = "../core/finality-grandpa/primitives" } substrate-client = { path = "../core/client" } basic-authorship = { package = "substrate-basic-authorship", path = "../core/basic-authorship" } node-template-runtime = { path = "runtime" } diff --git a/node-template/runtime/Cargo.toml b/node-template/runtime/Cargo.toml index 878374cf6a5f9..437da9d973af0 100644 --- a/node-template/runtime/Cargo.toml +++ b/node-template/runtime/Cargo.toml @@ -13,6 +13,7 @@ runtime-io = { package = "sr-io", path = "../../core/sr-io", default_features = version = { package = "sr-version", path = "../../core/sr-version", default_features = false } support = { package = "srml-support", path = "../../srml/support", default_features = false } primitives = { package = "substrate-primitives", path = "../../core/primitives", default_features = false } +substrate-session = { path = "../../core/session", default-features = false } balances = { package = "srml-balances", path = "../../srml/balances", default_features = false } aura = { package = "srml-aura", path = "../../srml/aura", default_features = false } executive = { package = "srml-executive", path = "../../srml/executive", default_features = false } @@ -50,5 +51,6 @@ std = [ "safe-mix/std", "consensus-aura/std", "offchain-primitives/std", + "substrate-session/std", ] no_std = [] diff --git a/node-template/runtime/src/lib.rs b/node-template/runtime/src/lib.rs index 7b2201c8efe99..2b9220c6e585b 100644 --- a/node-template/runtime/src/lib.rs +++ b/node-template/runtime/src/lib.rs @@ -9,8 +9,11 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use rstd::prelude::*; -use primitives::{ed25519, sr25519, OpaqueMetadata}; -use sr_primitives::{ApplyResult, transaction_validity::TransactionValidity, generic, create_runtime_str}; +use primitives::{sr25519, OpaqueMetadata, crypto::key_types}; +use sr_primitives::{ + ApplyResult, transaction_validity::TransactionValidity, generic, create_runtime_str, + impl_opaque_keys, +}; use sr_primitives::traits::{NumberFor, BlakeTwo256, Block as BlockT, StaticLookup, Verify, ConvertInto}; use sr_primitives::weights::Weight; use client::{ @@ -30,10 +33,10 @@ pub use sr_primitives::{Permill, Perbill}; pub use support::{StorageValue, construct_runtime, parameter_types}; /// Alias to the signature scheme used for Aura authority signatures. -pub type AuraSignature = ed25519::Signature; +pub type AuraSignature = consensus_aura::sr25519::AuthoritySignature; /// The Ed25519 pub key of an session that belongs to an Aura authority of the chain. -pub type AuraId = ed25519::Public; +pub type AuraId = consensus_aura::sr25519::AuthorityId; /// Alias to pubkey that identifies an account on the chain. pub type AccountId = ::Signer; @@ -71,8 +74,13 @@ pub mod opaque { pub type Block = generic::Block; /// Opaque block identifier type. pub type BlockId = generic::BlockId; - /// Opaque session key type. - pub type SessionKey = AuraId; + + impl_opaque_keys! { + pub struct SessionKeys { + #[id(key_types::AURA)] + pub aura: AuraId, + } + } } /// This runtime version. @@ -104,6 +112,8 @@ parameter_types! { impl system::Trait for Runtime { /// The identifier used to distinguish between accounts. type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type Call = Call; /// The lookup mechanism to get account ID from whatever is passed in dispatchers. type Lookup = Indices; /// The index type for storing how many extrinsics an account has signed. @@ -152,6 +162,7 @@ impl indices::Trait for Runtime { parameter_types! { pub const MinimumPeriod: u64 = 5000; } + impl timestamp::Trait for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; @@ -299,4 +310,11 @@ impl_runtime_apis! { Executive::offchain_worker(n) } } + + impl substrate_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + let seed = seed.as_ref().map(|s| rstd::str::from_utf8(&s).expect("Seed is an utf8 string")); + opaque::SessionKeys::generate(seed) + } + } } diff --git a/node-template/runtime/src/template.rs b/node-template/runtime/src/template.rs index d0c51443904d9..baf157a4fe8ae 100644 --- a/node-template/runtime/src/template.rs +++ b/node-template/runtime/src/template.rs @@ -93,6 +93,7 @@ mod tests { } impl system::Trait for Test { type Origin = Origin; + type Call = (); type Index = u64; type BlockNumber = u64; type Hash = H256; diff --git a/node-template/src/chain_spec.rs b/node-template/src/chain_spec.rs index 3970522b37ab5..65caab70dbe5f 100644 --- a/node-template/src/chain_spec.rs +++ b/node-template/src/chain_spec.rs @@ -1,8 +1,9 @@ -use primitives::{ed25519, sr25519, Pair}; +use primitives::{sr25519, Pair}; use node_template_runtime::{ AccountId, GenesisConfig, AuraConfig, BalancesConfig, SudoConfig, IndicesConfig, SystemConfig, WASM_BINARY, AuraId }; +use aura_primitives::sr25519::AuthorityPair as AuraPair; use substrate_service; // Note this is the URL for the telemetry server @@ -23,7 +24,7 @@ pub enum Alternative { } fn authority_key(s: &str) -> AuraId { - ed25519::Pair::from_string(&format!("//{}", s), None) + AuraPair::from_string(&format!("//{}", s), None) .expect("static values are valid; qed") .public() } diff --git a/node-template/src/service.rs b/node-template/src/service.rs index e5d83a189397d..31323e11adf28 100644 --- a/node-template/src/service.rs +++ b/node-template/src/service.rs @@ -15,7 +15,7 @@ use basic_authorship::ProposerFactory; use consensus::{import_queue, start_aura, AuraImportQueue, SlotDuration}; use futures::prelude::*; use substrate_client::{self as client, LongestChain}; -use primitives::{ed25519::Pair, Pair as PairT}; +use primitives::{Pair as PairT}; use inherents::InherentDataProviders; use network::{config::DummyFinalityProofRequestBuilder, construct_simple_protocol}; use substrate_executor::native_executor_instance; @@ -43,8 +43,6 @@ construct_simple_protocol! { construct_service_factory! { struct Factory { Block = Block, - ConsensusPair = Pair, - FinalityPair = Pair, RuntimeApi = RuntimeApi, NetworkProtocol = NodeProtocol { |config| Ok(NodeProtocol::new()) }, RuntimeDispatch = Executor, @@ -68,7 +66,7 @@ construct_service_factory! { }, AuthoritySetup = { |service: Self::FullService| { - if let Some(key) = service.authority_key() { + if let Some(key) = None:: { info!("Using authority key {}", key.public()); let proposer = ProposerFactory { client: service.client(), @@ -100,7 +98,7 @@ construct_service_factory! { Self::Block, > { |config: &mut FactoryFullConfiguration , client: Arc>, _select_chain: Self::SelectChain| { - import_queue::<_, _, Pair>( + import_queue::<_, _, aura_primitives::sr25519::AuthorityPair>( SlotDuration::get_or_compute(&*client)?, Box::new(client.clone()), None, @@ -115,7 +113,7 @@ construct_service_factory! { > { |config: &mut FactoryFullConfiguration, client: Arc>| { let fprb = Box::new(DummyFinalityProofRequestBuilder::default()) as Box<_>; - import_queue::<_, _, Pair>( + import_queue::<_, _, aura_primitives::sr25519::AuthorityPair>( SlotDuration::get_or_compute(&*client)?, Box::new(client.clone()), None, diff --git a/node/cli/Cargo.toml b/node/cli/Cargo.toml index f95f688ac3757..184ef9382d7f1 100644 --- a/node/cli/Cargo.toml +++ b/node/cli/Cargo.toml @@ -30,7 +30,6 @@ grandpa = { package = "substrate-finality-grandpa", path = "../../core/finality- grandpa_primitives = { package = "substrate-finality-grandpa-primitives", path = "../../core/finality-grandpa/primitives" } sr-primitives = { path = "../../core/sr-primitives" } node-executor = { path = "../executor" } -substrate-keystore = { path = "../../core/keystore" } substrate-telemetry = { package = "substrate-telemetry", path = "../../core/telemetry" } structopt = "0.2" transaction-factory = { path = "../../test-utils/transaction-factory" } @@ -42,12 +41,16 @@ finality_tracker = { package = "srml-finality-tracker", path = "../../srml/final contracts = { package = "srml-contracts", path = "../../srml/contracts" } system = { package = "srml-system", path = "../../srml/system" } balances = { package = "srml-balances", path = "../../srml/balances" } +support = { package = "srml-support", path = "../../srml/support", default-features = false } +im_online = { package = "srml-im-online", path = "../../srml/im-online", default-features = false } [dev-dependencies] +keystore = { package = "substrate-keystore", path = "../../core/keystore" } babe = { package = "substrate-consensus-babe", path = "../../core/consensus/babe", features = ["test-helpers"] } consensus-common = { package = "substrate-consensus-common", path = "../../core/consensus/common" } service-test = { package = "substrate-service-test", path = "../../core/service/test" } futures03 = { package = "futures-preview", version = "0.3.0-alpha.17" } +tempfile = "3.1" [build-dependencies] cli = { package = "substrate-cli", path = "../../core/cli" } diff --git a/node/cli/src/chain_spec.rs b/node/cli/src/chain_spec.rs index 6372146a45ecd..c14a2709c1120 100644 --- a/node/cli/src/chain_spec.rs +++ b/node/cli/src/chain_spec.rs @@ -16,9 +16,8 @@ //! Substrate chain configurations. -use babe_primitives::AuthorityId as BabeId; -use primitives::{ed25519, sr25519, Pair, crypto::UncheckedInto}; -use node_primitives::{AccountId, Balance}; +use primitives::{Pair, Public, crypto::UncheckedInto}; +pub use node_primitives::{AccountId, Balance}; use node_runtime::{ BabeConfig, BalancesConfig, ContractsConfig, CouncilConfig, DemocracyConfig, ElectionsConfig, GrandpaConfig, ImOnlineConfig, IndicesConfig, Perbill, @@ -30,7 +29,9 @@ pub use node_runtime::GenesisConfig; use substrate_service; use hex_literal::hex; use substrate_telemetry::TelemetryEndpoints; -use grandpa::AuthorityId as GrandpaId; +use grandpa_primitives::{AuthorityId as GrandpaId}; +use babe_primitives::{AuthorityId as BabeId}; +use im_online::AuthorityId as ImOnlineId; const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; @@ -42,11 +43,8 @@ pub fn flaming_fir_config() -> Result { ChainSpec::from_json_bytes(&include_bytes!("../res/flaming-fir.json")[..]) } -fn session_keys(ed_key: ed25519::Public, sr_key: sr25519::Public) -> SessionKeys { - SessionKeys { - ed25519: ed_key, - sr25519: sr_key, - } +fn session_keys(grandpa: GrandpaId, babe: BabeId, im_online: ImOnlineId) -> SessionKeys { + SessionKeys { grandpa, babe, im_online, } } fn staging_testnet_config_genesis() -> GenesisConfig { @@ -56,7 +54,7 @@ fn staging_testnet_config_genesis() -> GenesisConfig { // and // for i in 1 2 3 4 ; do for j in session; do subkey --ed25519 inspect "$secret"//fir//$j//$i; done; done - let initial_authorities: Vec<(AccountId, AccountId, BabeId, GrandpaId)> = vec![( + let initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId)> = vec![( // 5Fbsd6WXDGiLTxunqeK5BATNiocfCqu9bS1yArVjCgeBLkVy hex!["9c7a2ee14e565db0c69f78c7b4cd839fbf52b607d867e9e9c5a79042898a0d12"].unchecked_into(), // 5EnCiV7wSHeNhjW3FSUwiJNkcc2SBkPLn5Nj93FmbLtBjQUq @@ -65,6 +63,8 @@ fn staging_testnet_config_genesis() -> GenesisConfig { hex!["6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106"].unchecked_into(), // 5Fb9ayurnxnaXj56CjmyQLBiadfRCqUbL2VWNbbe1nZU6wiC hex!["9becad03e6dcac03cee07edebca5475314861492cdfc96a2144a67bbe9699332"].unchecked_into(), + // 5Fb9ayurnxnaXj56CjmyQLBiadfRCqUbL2VWNbbe1nZU6wiC + hex!["9becad03e6dcac03cee07edebca5475314861492cdfc96a2144a67bbe9699332"].unchecked_into(), ),( // 5ERawXCzCWkjVq3xz1W5KGNtVx2VdefvZ62Bw1FEuZW4Vny2 hex!["68655684472b743e456907b398d3a44c113f189e56d1bbfd55e889e295dfde78"].unchecked_into(), @@ -74,6 +74,8 @@ fn staging_testnet_config_genesis() -> GenesisConfig { hex!["482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e"].unchecked_into(), // 5EockCXN6YkiNCDjpqqnbcqd4ad35nU4RmA1ikM4YeRN4WcE hex!["7932cff431e748892fa48e10c63c17d30f80ca42e4de3921e641249cd7fa3c2f"].unchecked_into(), + // 5EockCXN6YkiNCDjpqqnbcqd4ad35nU4RmA1ikM4YeRN4WcE + hex!["7932cff431e748892fa48e10c63c17d30f80ca42e4de3921e641249cd7fa3c2f"].unchecked_into(), ),( // 5DyVtKWPidondEu8iHZgi6Ffv9yrJJ1NDNLom3X9cTDi98qp hex!["547ff0ab649283a7ae01dbc2eb73932eba2fb09075e9485ff369082a2ff38d65"].unchecked_into(), @@ -83,6 +85,8 @@ fn staging_testnet_config_genesis() -> GenesisConfig { hex!["482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a"].unchecked_into(), // 5E1jLYfLdUQKrFrtqoKgFrRvxM3oQPMbf6DfcsrugZZ5Bn8d hex!["5633b70b80a6c8bb16270f82cca6d56b27ed7b76c8fd5af2986a25a4788ce440"].unchecked_into(), + // 5E1jLYfLdUQKrFrtqoKgFrRvxM3oQPMbf6DfcsrugZZ5Bn8d + hex!["5633b70b80a6c8bb16270f82cca6d56b27ed7b76c8fd5af2986a25a4788ce440"].unchecked_into(), ),( // 5HYZnKWe5FVZQ33ZRJK1rG3WaLMztxWrrNDb1JRwaHHVWyP9 hex!["f26cdb14b5aec7b2789fd5ca80f979cef3761897ae1f37ffb3e154cbcc1c2663"].unchecked_into(), @@ -92,6 +96,8 @@ fn staging_testnet_config_genesis() -> GenesisConfig { hex!["00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378"].unchecked_into(), // 5DMa31Hd5u1dwoRKgC4uvqyrdK45RHv3CpwvpUC1EzuwDit4 hex!["3919132b851ef0fd2dae42a7e734fe547af5a6b809006100f48944d7fae8e8ef"].unchecked_into(), + // 5DMa31Hd5u1dwoRKgC4uvqyrdK45RHv3CpwvpUC1EzuwDit4 + hex!["3919132b851ef0fd2dae42a7e734fe547af5a6b809006100f48944d7fae8e8ef"].unchecked_into(), )]; // generated with secret: subkey inspect "$secret"/fir @@ -122,7 +128,7 @@ fn staging_testnet_config_genesis() -> GenesisConfig { }), session: Some(SessionConfig { keys: initial_authorities.iter().map(|x| { - (x.0.clone(), session_keys(x.3.clone(), x.2.clone())) + (x.0.clone(), session_keys(x.2.clone(), x.3.clone(), x.4.clone())) }).collect::>(), }), staking: Some(StakingConfig { @@ -159,15 +165,16 @@ fn staging_testnet_config_genesis() -> GenesisConfig { key: endowed_accounts[0].clone(), }), babe: Some(BabeConfig { - authorities: initial_authorities.iter().map(|x| (x.2.clone(), 1)).collect(), + authorities: initial_authorities.iter().map(|x| (x.3.clone(), 1)).collect(), }), im_online: Some(ImOnlineConfig { gossip_at: 0, - last_new_era_start: 0, + keys: initial_authorities.iter().map(|x| x.4.clone()).collect(), }), grandpa: Some(GrandpaConfig { - authorities: initial_authorities.iter().map(|x| (x.3.clone(), 1)).collect(), + authorities: initial_authorities.iter().map(|x| (x.2.clone(), 1)).collect(), }), + membership_Instance1: Some(Default::default()), } } @@ -186,58 +193,46 @@ pub fn staging_testnet_config() -> ChainSpec { ) } -/// Helper function to generate AccountId from seed -pub fn get_account_id_from_seed(seed: &str) -> AccountId { - sr25519::Pair::from_string(&format!("//{}", seed), None) +/// Helper function to generate a crypto pair from seed +pub fn get_from_seed(seed: &str) -> ::Public { + TPublic::Pair::from_string(&format!("//{}", seed), None) .expect("static values are valid; qed") .public() } -/// Helper function to generate BabeId from seed -pub fn get_babe_id_from_seed(seed: &str) -> BabeId { - sr25519::Pair::from_string(&format!("//{}", seed), None) - .expect("static values are valid; qed") - .public() -} - -/// Helper function to generate GrandpaId from seed -pub fn get_grandpa_id_from_seed(seed: &str) -> GrandpaId { - ed25519::Pair::from_string(&format!("//{}", seed), None) - .expect("static values are valid; qed") - .public() -} /// Helper function to generate stash, controller and session key from seed -pub fn get_authority_keys_from_seed(seed: &str) -> (AccountId, AccountId, BabeId, GrandpaId) { +pub fn get_authority_keys_from_seed(seed: &str) -> (AccountId, AccountId, GrandpaId, BabeId, ImOnlineId) { ( - get_account_id_from_seed(&format!("{}//stash", seed)), - get_account_id_from_seed(seed), - get_babe_id_from_seed(seed), - get_grandpa_id_from_seed(seed) + get_from_seed::(&format!("{}//stash", seed)), + get_from_seed::(seed), + get_from_seed::(seed), + get_from_seed::(seed), + get_from_seed::(seed), ) } /// Helper function to create GenesisConfig for testing pub fn testnet_genesis( - initial_authorities: Vec<(AccountId, AccountId, BabeId, GrandpaId)>, + initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId)>, root_key: AccountId, endowed_accounts: Option>, enable_println: bool, ) -> GenesisConfig { let endowed_accounts: Vec = endowed_accounts.unwrap_or_else(|| { vec![ - get_account_id_from_seed("Alice"), - get_account_id_from_seed("Bob"), - get_account_id_from_seed("Charlie"), - get_account_id_from_seed("Dave"), - get_account_id_from_seed("Eve"), - get_account_id_from_seed("Ferdie"), - get_account_id_from_seed("Alice//stash"), - get_account_id_from_seed("Bob//stash"), - get_account_id_from_seed("Charlie//stash"), - get_account_id_from_seed("Dave//stash"), - get_account_id_from_seed("Eve//stash"), - get_account_id_from_seed("Ferdie//stash"), + get_from_seed::("Alice"), + get_from_seed::("Bob"), + get_from_seed::("Charlie"), + get_from_seed::("Dave"), + get_from_seed::("Eve"), + get_from_seed::("Ferdie"), + get_from_seed::("Alice//stash"), + get_from_seed::("Bob//stash"), + get_from_seed::("Charlie//stash"), + get_from_seed::("Dave//stash"), + get_from_seed::("Eve//stash"), + get_from_seed::("Ferdie//stash"), ] }); @@ -260,7 +255,7 @@ pub fn testnet_genesis( }), session: Some(SessionConfig { keys: initial_authorities.iter().map(|x| { - (x.0.clone(), session_keys(x.3.clone(), x.2.clone())) + (x.0.clone(), session_keys(x.2.clone(), x.3.clone(), x.4.clone())) }).collect::>(), }), staking: Some(StakingConfig { @@ -302,15 +297,16 @@ pub fn testnet_genesis( key: root_key, }), babe: Some(BabeConfig { - authorities: initial_authorities.iter().map(|x| (x.2.clone(), 1)).collect(), + authorities: initial_authorities.iter().map(|x| (x.3.clone(), 1)).collect(), }), im_online: Some(ImOnlineConfig{ gossip_at: 0, - last_new_era_start: 0, + keys: initial_authorities.iter().map(|x| x.4.clone()).collect(), }), grandpa: Some(GrandpaConfig { - authorities: initial_authorities.iter().map(|x| (x.3.clone(), 1)).collect(), + authorities: initial_authorities.iter().map(|x| (x.2.clone(), 1)).collect(), }), + membership_Instance1: Some(Default::default()), } } @@ -319,7 +315,7 @@ fn development_config_genesis() -> GenesisConfig { vec![ get_authority_keys_from_seed("Alice"), ], - get_account_id_from_seed("Alice"), + get_from_seed::("Alice"), None, true, ) @@ -336,7 +332,7 @@ fn local_testnet_genesis() -> GenesisConfig { get_authority_keys_from_seed("Alice"), get_authority_keys_from_seed("Bob"), ], - get_account_id_from_seed("Alice"), + get_from_seed::("Alice"), None, false, ) @@ -358,7 +354,7 @@ pub(crate) mod tests { vec![ get_authority_keys_from_seed("Alice"), ], - get_account_id_from_seed("Alice"), + get_from_seed::("Alice"), None, false, ) @@ -380,7 +376,16 @@ pub(crate) mod tests { /// Local testnet config (multivalidator Alice + Bob) pub fn integration_test_config_with_two_authorities() -> ChainSpec { - ChainSpec::from_genesis("Integration Test", "test", local_testnet_genesis, vec![], None, None, None, None) + ChainSpec::from_genesis( + "Integration Test", + "test", + local_testnet_genesis, + vec![], + None, + None, + None, + None, + ) } #[test] diff --git a/node/cli/src/service.rs b/node/cli/src/service.rs index 0b887bb55a666..86247668f7550 100644 --- a/node/cli/src/service.rs +++ b/node/cli/src/service.rs @@ -22,12 +22,9 @@ use std::sync::Arc; use std::time::Duration; use babe::{import_queue, start_babe, BabeImportQueue, Config}; -use babe_primitives::AuthorityPair as BabePair; use client::{self, LongestChain}; use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider}; use node_executor; -use primitives::Pair; -use grandpa_primitives::AuthorityPair as GrandpaPair; use futures::prelude::*; use node_primitives::Block; use node_runtime::{GenesisConfig, RuntimeApi}; @@ -40,7 +37,6 @@ use transaction_pool::{self, txpool::{Pool as TransactionPool}}; use inherents::InherentDataProviders; use network::construct_simple_protocol; use substrate_service::construct_service_factory; -use log::info; use substrate_service::TelemetryOnConnect; construct_simple_protocol! { @@ -89,8 +85,6 @@ impl Default for NodeConfig where F: substrate_service::ServiceFactory { construct_service_factory! { struct Factory { Block = Block, - ConsensusPair = BabePair, - FinalityPair = GrandpaPair, RuntimeApi = RuntimeApi, NetworkProtocol = NodeProtocol { |config| Ok(NodeProtocol::new()) }, RuntimeDispatch = node_executor::Executor, @@ -119,74 +113,59 @@ construct_service_factory! { } } - if let Some(babe_key) = service.authority_key() { - info!("Using BABE key {}", babe_key.public()); - - let proposer = substrate_basic_authorship::ProposerFactory { - client: service.client(), - transaction_pool: service.transaction_pool(), - }; - - let client = service.client(); - let select_chain = service.select_chain() - .ok_or(ServiceError::SelectChainRequired)?; - - let babe_config = babe::BabeParams { - config: Config::get_or_compute(&*client)?, - local_key: Arc::new(babe_key), - client, - select_chain, - block_import, - env: proposer, - sync_oracle: service.network(), - inherent_data_providers: service.config.custom.inherent_data_providers.clone(), - force_authoring: service.config.force_authoring, - time_source: babe_link, - }; + let proposer = substrate_basic_authorship::ProposerFactory { + client: service.client(), + transaction_pool: service.transaction_pool(), + }; - let babe = start_babe(babe_config)?; - let select = babe.select(service.on_exit()).then(|_| Ok(())); - service.spawn_task(Box::new(select)); - } + let client = service.client(); + let select_chain = service.select_chain().ok_or(ServiceError::SelectChainRequired)?; - let grandpa_key = if service.config.disable_grandpa { - None - } else { - service.fg_authority_key() + let babe_config = babe::BabeParams { + config: Config::get_or_compute(&*client)?, + keystore: service.keystore(), + client, + select_chain, + block_import, + env: proposer, + sync_oracle: service.network(), + inherent_data_providers: service.config.custom.inherent_data_providers.clone(), + force_authoring: service.config.force_authoring, + time_source: babe_link, }; + let babe = start_babe(babe_config)?; + let select = babe.select(service.on_exit()).then(|_| Ok(())); + service.spawn_task(Box::new(select)); + let config = grandpa::Config { - local_key: grandpa_key.map(Arc::new), // FIXME #1578 make this available through chainspec gossip_duration: Duration::from_millis(333), justification_period: 4096, - name: Some(service.config.name.clone()) + name: Some(service.config.name.clone()), + keystore: Some(service.keystore()), }; - match config.local_key { - None if !service.config.grandpa_voter => { - service.spawn_task(Box::new(grandpa::run_grandpa_observer( - config, - link_half, - service.network(), - service.on_exit(), - )?)); - }, - // Either config.local_key is set, or user forced voter service via `--grandpa-voter` flag. - _ => { - let telemetry_on_connect = TelemetryOnConnect { - telemetry_connection_sinks: service.telemetry_on_connect_stream(), - }; - let grandpa_config = grandpa::GrandpaParams { - config: config, - link: link_half, - network: service.network(), - inherent_data_providers: service.config.custom.inherent_data_providers.clone(), - on_exit: service.on_exit(), - telemetry_on_connect: Some(telemetry_on_connect), - }; - service.spawn_task(Box::new(grandpa::run_grandpa_voter(grandpa_config)?)); - }, + if service.config.roles.is_authority() { + let telemetry_on_connect = TelemetryOnConnect { + telemetry_connection_sinks: service.telemetry_on_connect_stream(), + }; + let grandpa_config = grandpa::GrandpaParams { + config: config, + link: link_half, + network: service.network(), + inherent_data_providers: service.config.custom.inherent_data_providers.clone(), + on_exit: service.on_exit(), + telemetry_on_connect: Some(telemetry_on_connect), + }; + service.spawn_task(Box::new(grandpa::run_grandpa_voter(grandpa_config)?)); + } else if !service.config.grandpa_voter { + service.spawn_task(Box::new(grandpa::run_grandpa_observer( + config, + link_half, + service.network(), + service.on_exit(), + )?)); } Ok(service) @@ -273,7 +252,7 @@ mod tests { use sr_primitives::{generic::{BlockId, Era, Digest}, traits::Block, OpaqueExtrinsic}; use timestamp; use finality_tracker; - use keyring::{AccountKeyring, Sr25519Keyring}; + use keyring::AccountKeyring; use substrate_service::ServiceFactory; use service_test::SyncService; use crate::service::Factory; @@ -339,9 +318,13 @@ mod tests { #[test] #[ignore] fn test_sync() { + let keystore_path = tempfile::tempdir().expect("Creates keystore path"); + let keystore = keystore::Store::open(keystore_path.path(), None).expect("Creates keystore"); + let alice = keystore.write().insert_ephemeral_from_seed::("//Alice") + .expect("Creates authority pair"); + let chain_spec = crate::chain_spec::tests::integration_test_config_with_single_authority(); - let alice = Arc::new(Sr25519Keyring::Alice.pair()); let mut slot_num = 1u64; let block_factory = |service: &SyncService<::FullService>| { let service = service.get(); @@ -370,8 +353,8 @@ mod tests { &*service.client(), &parent_id, slot_num, - &alice, (278, 1000), + &keystore, ) { break babe_pre_digest; } @@ -395,7 +378,7 @@ mod tests { let to_sign = pre_hash.encode(); let signature = alice.sign(&to_sign[..]); let item = ::babe_seal( - signature, + signature.into(), ); slot_num += 1; diff --git a/node/executor/src/lib.rs b/node/executor/src/lib.rs index 711fdcdb698dc..d012d35778693 100644 --- a/node/executor/src/lib.rs +++ b/node/executor/src/lib.rs @@ -321,20 +321,20 @@ mod tests { sr25519_keyring: &Sr25519Keyring, ) -> SessionKeys { SessionKeys { - ed25519: ed25519_keyring.to_owned().into(), - sr25519: sr25519_keyring.to_owned().into(), + grandpa: ed25519_keyring.to_owned().public().into(), + babe: sr25519_keyring.to_owned().public().into(), + im_online: sr25519_keyring.to_owned().public().into(), } } fn new_test_ext(code: &[u8], support_changes_trie: bool) -> TestExternalities { let mut ext = TestExternalities::new_with_code_with_children(code, GenesisConfig { - babe: Some(Default::default()), system: Some(SystemConfig { changes_trie_config: if support_changes_trie { Some(ChangesTrieConfiguration { digest_interval: 2, digest_levels: 2, }) } else { None }, - ..Default::default() + .. Default::default() }), indices: Some(IndicesConfig { ids: vec![alice(), bob(), charlie(), dave(), eve(), ferdie()], @@ -379,19 +379,21 @@ mod tests { offline_slash_grace: 0, invulnerables: vec![alice(), bob(), charlie()], }), - democracy: Some(Default::default()), - collective_Instance1: Some(Default::default()), - collective_Instance2: Some(Default::default()), - elections: Some(Default::default()), contracts: Some(ContractsConfig { current_schedule: Default::default(), gas_price: 1 * MILLICENTS, }), - sudo: Some(Default::default()), - im_online: Some(Default::default()), + babe: Some(Default::default()), grandpa: Some(GrandpaConfig { authorities: vec![], }), + im_online: Some(Default::default()), + democracy: Some(Default::default()), + collective_Instance1: Some(Default::default()), + collective_Instance2: Some(Default::default()), + membership_Instance1: Some(Default::default()), + elections: Some(Default::default()), + sudo: Some(Default::default()), }.build_storage().unwrap()); ext.changes_trie_storage().insert(0, GENESIS_HASH.into(), Default::default()); ext diff --git a/node/runtime/Cargo.toml b/node/runtime/Cargo.toml index da6180889ed99..7ffb29e0784ce 100644 --- a/node/runtime/Cargo.toml +++ b/node/runtime/Cargo.toml @@ -29,6 +29,7 @@ executive = { package = "srml-executive", path = "../../srml/executive", default finality-tracker = { package = "srml-finality-tracker", path = "../../srml/finality-tracker", default-features = false } grandpa = { package = "srml-grandpa", path = "../../srml/grandpa", default-features = false } indices = { package = "srml-indices", path = "../../srml/indices", default-features = false } +membership = { package = "srml-membership", path = "../../srml/membership", default-features = false } session = { package = "srml-session", path = "../../srml/session", default-features = false, features = ["historical"] } staking = { package = "srml-staking", path = "../../srml/staking", default-features = false } system = { package = "srml-system", path = "../../srml/system", default-features = false } @@ -40,6 +41,7 @@ node-primitives = { path = "../primitives", default-features = false } rustc-hex = { version = "2.0", optional = true } serde = { version = "1.0", optional = true } substrate-keyring = { path = "../../core/keyring", optional = true } +substrate-session = { path = "../../core/session", default-features = false } [build-dependencies] wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.2", path = "../../core/utils/wasm-builder-runner" } @@ -68,6 +70,7 @@ std = [ "finality-tracker/std", "grandpa/std", "indices/std", + "membership/std", "session/std", "staking/std", "system/std", @@ -83,4 +86,5 @@ std = [ "substrate-keyring", "offchain-primitives/std", "im-online/std", + "substrate-session/std", ] diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index c2c52529692b8..0ecbec76018a6 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -47,6 +47,7 @@ use elections::VoteIndex; use version::NativeVersion; use primitives::OpaqueMetadata; use grandpa::{AuthorityId as GrandpaId, AuthorityWeight as GrandpaWeight}; +use im_online::{AuthorityId as ImOnlineId}; use finality_tracker::{DEFAULT_REPORT_LATENCY, DEFAULT_WINDOW_SIZE}; #[cfg(any(feature = "std", test))] @@ -79,8 +80,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to equal spec_version. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 130, - impl_version: 130, + spec_version: 131, + impl_version: 131, apis: RUNTIME_API_VERSIONS, }; @@ -111,6 +112,7 @@ parameter_types! { impl system::Trait for Runtime { type Origin = Origin; + type Call = Call; type Index = Index; type BlockNumber = BlockNumber; type Hash = Hash; @@ -191,10 +193,12 @@ type SessionHandlers = (Grandpa, Babe, ImOnline); impl_opaque_keys! { pub struct SessionKeys { - #[id(key_types::ED25519)] - pub ed25519: GrandpaId, - #[id(key_types::SR25519)] - pub sr25519: BabeId, + #[id(key_types::GRANDPA)] + pub grandpa: GrandpaId, + #[id(key_types::BABE)] + pub babe: BabeId, + #[id(key_types::IM_ONLINE)] + pub im_online: ImOnlineId, } } @@ -244,7 +248,7 @@ parameter_types! { pub const EmergencyVotingPeriod: BlockNumber = 3 * 24 * 60 * MINUTES; pub const MinimumDeposit: Balance = 100 * DOLLARS; pub const EnactmentPeriod: BlockNumber = 30 * 24 * 60 * MINUTES; - pub const CooloffPeriod: BlockNumber = 30 * 24 * 60 * MINUTES; + pub const CooloffPeriod: BlockNumber = 28 * 24 * 60 * MINUTES; } impl democracy::Trait for Runtime { @@ -256,17 +260,26 @@ impl democracy::Trait for Runtime { type VotingPeriod = VotingPeriod; type EmergencyVotingPeriod = EmergencyVotingPeriod; type MinimumDeposit = MinimumDeposit; - type ExternalOrigin = collective::EnsureProportionAtLeast<_1, _2, AccountId, CouncilInstance>; - type ExternalMajorityOrigin = collective::EnsureProportionAtLeast<_2, _3, AccountId, CouncilInstance>; - type ExternalPushOrigin = collective::EnsureProportionAtLeast<_2, _3, AccountId, TechnicalInstance>; - type EmergencyOrigin = collective::EnsureProportionAtLeast<_1, _1, AccountId, CouncilInstance>; - type CancellationOrigin = collective::EnsureProportionAtLeast<_2, _3, AccountId, CouncilInstance>; - type VetoOrigin = collective::EnsureMember; + /// A straight majority of the council can decide what their next motion is. + type ExternalOrigin = collective::EnsureProportionAtLeast<_1, _2, AccountId, CouncilCollective>; + /// A super-majority can have the next scheduled referendum be a straight majority-carries vote. + type ExternalMajorityOrigin = collective::EnsureProportionAtLeast<_3, _4, AccountId, CouncilCollective>; + /// A unanimous council can have the next scheduled referendum be a straight default-carries + /// (NTB) vote. + type ExternalDefaultOrigin = collective::EnsureProportionAtLeast<_1, _1, AccountId, CouncilCollective>; + /// Two thirds of the technical committee can have an ExternalMajority/ExternalDefault vote + /// be tabled immediately and with a shorter voting/enactment period. + type FastTrackOrigin = collective::EnsureProportionAtLeast<_2, _3, AccountId, TechnicalCollective>; + // To cancel a proposal which has been passed, 2/3 of the council must agree to it. + type CancellationOrigin = collective::EnsureProportionAtLeast<_2, _3, AccountId, CouncilCollective>; + // Any single technical committee member may veto a coming council proposal, however they can + // only do it once and it lasts only for the cooloff period. + type VetoOrigin = collective::EnsureMember; type CooloffPeriod = CooloffPeriod; } -type CouncilInstance = collective::Instance1; -impl collective::Trait for Runtime { +type CouncilCollective = collective::Instance1; +impl collective::Trait for Runtime { type Origin = Origin; type Proposal = Call; type Event = Event; @@ -302,13 +315,23 @@ impl elections::Trait for Runtime { type DecayRatio = DecayRatio; } -type TechnicalInstance = collective::Instance2; -impl collective::Trait for Runtime { +type TechnicalCollective = collective::Instance2; +impl collective::Trait for Runtime { type Origin = Origin; type Proposal = Call; type Event = Event; } +impl membership::Trait for Runtime { + type Event = Event; + type AddOrigin = collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; + type RemoveOrigin = collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; + type SwapOrigin = collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; + type ResetOrigin = collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; + type MembershipInitialized = TechnicalCommittee; + type MembershipChanged = TechnicalCommittee; +} + parameter_types! { pub const ProposalBond: Permill = Permill::from_percent(5); pub const ProposalBondMinimum: Balance = 1 * DOLLARS; @@ -318,8 +341,8 @@ parameter_types! { impl treasury::Trait for Runtime { type Currency = Balances; - type ApproveOrigin = collective::EnsureMembers<_4, AccountId, CouncilInstance>; - type RejectOrigin = collective::EnsureMembers<_2, AccountId, CouncilInstance>; + type ApproveOrigin = collective::EnsureMembers<_4, AccountId, CouncilCollective>; + type RejectOrigin = collective::EnsureMembers<_2, AccountId, CouncilCollective>; type Event = Event; type MintedForSpending = (); type ProposalRejection = (); @@ -369,12 +392,9 @@ impl sudo::Trait for Runtime { } impl im_online::Trait for Runtime { - type AuthorityId = BabeId; type Call = Call; type Event = Event; - type SessionsPerEra = SessionsPerEra; type UncheckedExtrinsic = UncheckedExtrinsic; - type IsValidAuthorityId = Babe; } impl grandpa::Trait for Runtime { @@ -410,12 +430,13 @@ construct_runtime!( Council: collective::::{Module, Call, Storage, Origin, Event, Config}, TechnicalCommittee: collective::::{Module, Call, Storage, Origin, Event, Config}, Elections: elections::{Module, Call, Storage, Event, Config}, + TechnicalMembership: membership::::{Module, Call, Storage, Event, Config}, FinalityTracker: finality_tracker::{Module, Call, Inherent}, Grandpa: grandpa::{Module, Call, Storage, Config, Event}, Treasury: treasury::{Module, Call, Storage, Event}, Contracts: contracts, Sudo: sudo, - ImOnline: im_online::{default, ValidateUnsigned}, + ImOnline: im_online::{Module, Call, Storage, Event, ValidateUnsigned, Config}, } ); @@ -547,4 +568,11 @@ impl_runtime_apis! { Babe::authorities().into_iter().map(|(a, _)| a).collect() } } + + impl substrate_session::SessionKeys for Runtime { + fn generate_session_keys(seed: Option>) -> Vec { + let seed = seed.as_ref().map(|s| rstd::str::from_utf8(&s).expect("Seed is an utf8 string")); + SessionKeys::generate(seed) + } + } } diff --git a/srml/assets/src/lib.rs b/srml/assets/src/lib.rs index 363e1c351cdea..bacf722d5d836 100644 --- a/srml/assets/src/lib.rs +++ b/srml/assets/src/lib.rs @@ -264,6 +264,7 @@ mod tests { impl system::Trait for Test { type Origin = Origin; type Index = u64; + type Call = (); type BlockNumber = u64; type Hash = H256; type Hashing = BlakeTwo256; diff --git a/srml/aura/Cargo.toml b/srml/aura/Cargo.toml index 6ce9c87c5c006..955831ba29910 100644 --- a/srml/aura/Cargo.toml +++ b/srml/aura/Cargo.toml @@ -11,6 +11,7 @@ inherents = { package = "substrate-inherents", path = "../../core/inherents", de rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } sr-primitives = { path = "../../core/sr-primitives", default-features = false } primitives = { package = "substrate-primitives", path = "../../core/primitives", default-features = false } +app-crypto = { package = "substrate-application-crypto", path = "../../core/application-crypto", default-features = false } srml-support = { path = "../support", default-features = false } system = { package = "srml-system", path = "../system", default-features = false } timestamp = { package = "srml-timestamp", path = "../timestamp", default-features = false } @@ -20,7 +21,7 @@ substrate-consensus-aura-primitives = { path = "../../core/consensus/aura/primit [dev-dependencies] lazy_static = "1.0" -parking_lot = "0.8.0" +parking_lot = "0.9.0" runtime_io = { package = "sr-io", path = "../../core/sr-io" } [features] @@ -37,4 +38,5 @@ std = [ "staking/std", "inherents/std", "substrate-consensus-aura-primitives/std", + "app-crypto/std", ] diff --git a/srml/aura/src/lib.rs b/srml/aura/src/lib.rs index a79b5c46a5a03..6d707fc8e195f 100644 --- a/srml/aura/src/lib.rs +++ b/srml/aura/src/lib.rs @@ -53,9 +53,9 @@ pub use timestamp; use rstd::{result, prelude::*}; use codec::Encode; use srml_support::{decl_storage, decl_module, Parameter, storage::StorageValue, traits::Get}; +use app_crypto::AppPublic; use sr_primitives::{ - traits::{SaturatedConversion, Saturating, Zero, One, Member, IsMember, TypedKey}, - generic::DigestItem, + traits::{SaturatedConversion, Saturating, Zero, One, Member, IsMember}, generic::DigestItem, }; use timestamp::OnTimestampSet; #[cfg(feature = "std")] @@ -156,7 +156,7 @@ pub trait Trait: timestamp::Trait { type HandleReport: HandleReport; /// The identifier type for an authority. - type AuthorityId: Member + Parameter + TypedKey + Default; + type AuthorityId: Member + Parameter + AppPublic + Default; } decl_storage! { diff --git a/srml/aura/src/mock.rs b/srml/aura/src/mock.rs index f00e090b686f1..7c5d862a50c8a 100644 --- a/srml/aura/src/mock.rs +++ b/srml/aura/src/mock.rs @@ -18,6 +18,8 @@ #![cfg(test)] +use crate::{Trait, Module, GenesisConfig}; +use substrate_consensus_aura_primitives::ed25519::AuthorityId; use sr_primitives::{ traits::IdentityLookup, Perbill, testing::{Header, UintAuthorityId}, @@ -25,7 +27,6 @@ use sr_primitives::{ use srml_support::{impl_outer_origin, parameter_types}; use runtime_io; use primitives::{H256, Blake2Hasher}; -use crate::{Trait, Module, GenesisConfig}; impl_outer_origin!{ pub enum Origin for Test {} @@ -47,6 +48,7 @@ impl system::Trait for Test { type Origin = Origin; type Index = u64; type BlockNumber = u64; + type Call = (); type Hash = H256; type Hashing = ::sr_primitives::traits::BlakeTwo256; type AccountId = u64; @@ -68,13 +70,13 @@ impl timestamp::Trait for Test { impl Trait for Test { type HandleReport = (); - type AuthorityId = UintAuthorityId; + type AuthorityId = AuthorityId; } pub fn new_test_ext(authorities: Vec) -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::default().build_storage::().unwrap().0; t.extend(GenesisConfig::{ - authorities: authorities.into_iter().map(|a| UintAuthorityId(a)).collect(), + authorities: authorities.into_iter().map(|a| UintAuthorityId(a).to_public_key()).collect(), }.build_storage().unwrap().0); t.into() } diff --git a/srml/authorship/src/lib.rs b/srml/authorship/src/lib.rs index e46e2f232deaa..fb0f451930555 100644 --- a/srml/authorship/src/lib.rs +++ b/srml/authorship/src/lib.rs @@ -349,6 +349,7 @@ mod tests { type Origin = Origin; type Index = u64; type BlockNumber = u64; + type Call = (); type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; diff --git a/srml/babe/Cargo.toml b/srml/babe/Cargo.toml index 2e2d3c84b0847..76bd849a95594 100644 --- a/srml/babe/Cargo.toml +++ b/srml/babe/Cargo.toml @@ -17,11 +17,11 @@ system = { package = "srml-system", path = "../system", default-features = false timestamp = { package = "srml-timestamp", path = "../timestamp", default-features = false } session = { package = "srml-session", path = "../session", default-features = false } babe-primitives = { package = "substrate-consensus-babe-primitives", path = "../../core/consensus/babe/primitives", default-features = false } -runtime_io = { package = "sr-io", path = "../../core/sr-io", default-features = false } +runtime_io = { package = "sr-io", path = "../../core/sr-io", default-features = false, features = [ "wasm-nice-panic-message" ] } [dev-dependencies] lazy_static = "1.3.0" -parking_lot = "0.8.0" +parking_lot = "0.9.0" primitives = { package = "substrate-primitives", path = "../../core/primitives" } [features] diff --git a/srml/balances/src/lib.rs b/srml/balances/src/lib.rs index b1aefccc67fca..4d0306af28e31 100644 --- a/srml/balances/src/lib.rs +++ b/srml/balances/src/lib.rs @@ -763,6 +763,7 @@ impl, I: Instance> PartialEq for ElevatedTrait { impl, I: Instance> Eq for ElevatedTrait {} impl, I: Instance> system::Trait for ElevatedTrait { type Origin = T::Origin; + type Call = T::Call; type Index = T::Index; type BlockNumber = T::BlockNumber; type Hash = T::Hash; @@ -1213,12 +1214,14 @@ impl, I: Instance> rstd::fmt::Debug for TakeFees { impl, I: Instance + Clone + Eq> SignedExtension for TakeFees { type AccountId = T::AccountId; + type Call = T::Call; type AdditionalSigned = (); fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) } fn validate( &self, who: &Self::AccountId, + _call: &Self::Call, info: DispatchInfo, len: usize, ) -> rstd::result::Result { diff --git a/srml/balances/src/mock.rs b/srml/balances/src/mock.rs index 2c84bb9afb3fe..ccca018ae02be 100644 --- a/srml/balances/src/mock.rs +++ b/srml/balances/src/mock.rs @@ -85,6 +85,7 @@ impl system::Trait for Runtime { type Origin = Origin; type Index = u64; type BlockNumber = u64; + type Call = (); type Hash = H256; type Hashing = ::sr_primitives::traits::BlakeTwo256; type AccountId = u64; @@ -208,6 +209,9 @@ impl ExtBuilder { pub type System = system::Module; pub type Balances = Module; + +pub const CALL: &::Call = &(); + /// create a transaction info struct from weight. Handy to avoid building the whole struct. pub fn info_from_weight(w: Weight) -> DispatchInfo { DispatchInfo { weight: w, ..Default::default() } diff --git a/srml/balances/src/tests.rs b/srml/balances/src/tests.rs index 5a977747fe068..edbb198f341ee 100644 --- a/srml/balances/src/tests.rs +++ b/srml/balances/src/tests.rs @@ -19,7 +19,7 @@ #![cfg(test)] use super::*; -use mock::{Balances, ExtBuilder, Runtime, System, info_from_weight}; +use mock::{Balances, ExtBuilder, Runtime, System, info_from_weight, CALL}; use runtime_io::with_externalities; use srml_support::{ assert_noop, assert_ok, assert_err, @@ -127,6 +127,7 @@ fn lock_reasons_should_work() { assert!( as SignedExtension>::pre_dispatch( TakeFees::from(1), &1, + CALL, info_from_weight(1), 0, ).is_ok()); @@ -140,6 +141,7 @@ fn lock_reasons_should_work() { assert!( as SignedExtension>::pre_dispatch( TakeFees::from(1), &1, + CALL, info_from_weight(1), 0, ).is_ok()); @@ -150,6 +152,7 @@ fn lock_reasons_should_work() { assert!( as SignedExtension>::pre_dispatch( TakeFees::from(1), &1, + CALL, info_from_weight(1), 0, ).is_err()); @@ -757,9 +760,9 @@ fn signed_extension_take_fees_work() { .build(), || { let len = 10; - assert!(TakeFees::::from(0).pre_dispatch(&1, info_from_weight(5), len).is_ok()); + assert!(TakeFees::::from(0).pre_dispatch(&1, CALL, info_from_weight(5), len).is_ok()); assert_eq!(Balances::free_balance(&1), 100 - 20 - 25); - assert!(TakeFees::::from(5 /* tipped */).pre_dispatch(&1, info_from_weight(3), len).is_ok()); + assert!(TakeFees::::from(5 /* tipped */).pre_dispatch(&1, CALL, info_from_weight(3), len).is_ok()); assert_eq!(Balances::free_balance(&1), 100 - 20 - 25 - 20 - 5 - 15); } ); @@ -777,7 +780,7 @@ fn signed_extension_take_fees_is_bounded() { use sr_primitives::weights::Weight; // maximum weight possible - assert!(TakeFees::::from(0).pre_dispatch(&1, info_from_weight(Weight::max_value()), 10).is_ok()); + assert!(TakeFees::::from(0).pre_dispatch(&1, CALL, info_from_weight(Weight::max_value()), 10).is_ok()); // fee will be proportional to what is the actual maximum weight in the runtime. assert_eq!( Balances::free_balance(&1), diff --git a/srml/collective/src/lib.rs b/srml/collective/src/lib.rs index 20c11bdf9b7f6..8c4820715bd25 100644 --- a/srml/collective/src/lib.rs +++ b/srml/collective/src/lib.rs @@ -16,6 +16,9 @@ //! Collective system: Members of a set of account IDs can make their collective feelings known //! through dispatched calls from one of two specialised origins. +//! +//! The membership can be provided in one of two ways: either directly, using the Root-dispatchable +//! function `set_members`, or indirectly, through implementing the `ChangeMembers` #![cfg_attr(not(feature = "std"), no_std)] #![recursion_limit="128"] @@ -119,7 +122,7 @@ decl_event!( } ); -// Note: this module is not benchmarked. The weights are obtained based on the similarity fo the +// Note: this module is not benchmarked. The weights are obtained based on the similarity of the // executed logic with other democracy function. Note that councillor operations are assigned to the // operational class. decl_module! { @@ -133,41 +136,12 @@ decl_module! { #[weight = SimpleDispatchInfo::FixedOperational(100_000)] fn set_members(origin, new_members: Vec) { ensure_root(origin)?; - - // stable sorting since they will generally be provided sorted. - let mut old_members = >::get(); - old_members.sort(); let mut new_members = new_members; new_members.sort(); - let mut old_iter = old_members.iter(); - let mut new_iter = new_members.iter(); - let mut incoming = vec![]; - let mut outgoing = vec![]; - let mut old_i = old_iter.next(); - let mut new_i = new_iter.next(); - loop { - match (old_i, new_i) { - (None, None) => break, - (Some(old), Some(new)) if old == new => { - old_i = old_iter.next(); - new_i = new_iter.next(); - } - (Some(old), Some(new)) if old < new => { - outgoing.push(old.clone()); - old_i = old_iter.next(); - } - (Some(old), None) => { - outgoing.push(old.clone()); - old_i = old_iter.next(); - } - (_, Some(new)) => { - incoming.push(new.clone()); - new_i = new_iter.next(); - } - } - } - - Self::change_members(&incoming, &outgoing, &new_members); + >::mutate(|m| { + >::set_members_sorted(&new_members[..], m); + *m = new_members; + }); } /// Dispatch a proposal from a member using the `Member` origin. @@ -287,18 +261,18 @@ impl, I: Instance> Module { } impl, I: Instance> ChangeMembers for Module { - fn change_members(_incoming: &[T::AccountId], outgoing: &[T::AccountId], new: &[T::AccountId]) { + fn change_members_sorted(_incoming: &[T::AccountId], outgoing: &[T::AccountId], new: &[T::AccountId]) { // remove accounts from all current voting in motions. - let mut old = outgoing.to_vec(); - old.sort_unstable(); + let mut outgoing = outgoing.to_vec(); + outgoing.sort_unstable(); for h in Self::proposals().into_iter() { >::mutate(h, |v| if let Some(mut votes) = v.take() { votes.ayes = votes.ayes.into_iter() - .filter(|i| old.binary_search(i).is_err()) + .filter(|i| outgoing.binary_search(i).is_err()) .collect(); votes.nays = votes.nays.into_iter() - .filter(|i| old.binary_search(i).is_err()) + .filter(|i| outgoing.binary_search(i).is_err()) .collect(); *v = Some(votes); } @@ -413,6 +387,7 @@ mod tests { type Origin = Origin; type Index = u64; type BlockNumber = u64; + type Call = (); type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; @@ -486,7 +461,7 @@ mod tests { Collective::voting(&hash), Some(Votes { index: 0, threshold: 3, ayes: vec![1, 2], nays: vec![] }) ); - Collective::change_members(&[4], &[1], &[2, 3, 4]); + Collective::change_members_sorted(&[4], &[1], &[2, 3, 4]); assert_eq!( Collective::voting(&hash), Some(Votes { index: 0, threshold: 3, ayes: vec![2], nays: vec![] }) @@ -500,7 +475,7 @@ mod tests { Collective::voting(&hash), Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![3] }) ); - Collective::change_members(&[], &[3], &[2, 4]); + Collective::change_members_sorted(&[], &[3], &[2, 4]); assert_eq!( Collective::voting(&hash), Some(Votes { index: 1, threshold: 2, ayes: vec![2], nays: vec![] }) diff --git a/srml/contracts/src/exec.rs b/srml/contracts/src/exec.rs index b27116a1ac510..eda37d2452485 100644 --- a/srml/contracts/src/exec.rs +++ b/srml/contracts/src/exec.rs @@ -275,7 +275,7 @@ pub enum DeferredAction { /// The account id of the contract who dispatched this call. origin: T::AccountId, /// The call to dispatch. - call: T::Call, + call: ::Call, }, RestoreTo { /// The account id of the contract which is removed during the restoration and transfers diff --git a/srml/contracts/src/lib.rs b/srml/contracts/src/lib.rs index d28ee362eb4a3..ccb6709384fb6 100644 --- a/srml/contracts/src/lib.rs +++ b/srml/contracts/src/lib.rs @@ -333,7 +333,7 @@ pub trait Trait: timestamp::Trait { /// /// It is recommended (though not required) for this function to return a fee that would be taken /// by the Executive module for regular dispatch. - type ComputeDispatchFee: ComputeDispatchFee>; + type ComputeDispatchFee: ComputeDispatchFee<::Call, BalanceOf>; /// trie id generator type TrieIdGenerator: TrieIdGenerator; @@ -427,8 +427,8 @@ where /// The default dispatch fee computor computes the fee in the same way that /// the implementation of `MakePayment` for the Balances module does. pub struct DefaultDispatchFeeComputor(PhantomData); -impl ComputeDispatchFee> for DefaultDispatchFeeComputor { - fn compute_dispatch_fee(call: &T::Call) -> BalanceOf { +impl ComputeDispatchFee<::Call, BalanceOf> for DefaultDispatchFeeComputor { + fn compute_dispatch_fee(call: &::Call) -> BalanceOf { let encoded_len = call.using_encoded(|encoded| encoded.len() as u32); let base_fee = T::TransactionBaseFee::get(); let byte_fee = T::TransactionByteFee::get(); diff --git a/srml/contracts/src/tests.rs b/srml/contracts/src/tests.rs index 80bb7af518e14..e12849f6fb6fc 100644 --- a/srml/contracts/src/tests.rs +++ b/srml/contracts/src/tests.rs @@ -107,6 +107,7 @@ impl system::Trait for Test { type Index = u64; type BlockNumber = u64; type Hash = H256; + type Call = (); type Hashing = BlakeTwo256; type AccountId = u64; type Lookup = IdentityLookup; diff --git a/srml/council/src/lib.rs b/srml/council/src/lib.rs index 6d2db9373d482..712b2ca835a80 100644 --- a/srml/council/src/lib.rs +++ b/srml/council/src/lib.rs @@ -107,6 +107,7 @@ mod tests { type Origin = Origin; type Index = u64; type BlockNumber = u64; + type Call = (); type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; diff --git a/srml/democracy/src/lib.rs b/srml/democracy/src/lib.rs index 332ad91358f9d..c3debc1f06c37 100644 --- a/srml/democracy/src/lib.rs +++ b/srml/democracy/src/lib.rs @@ -61,6 +61,8 @@ pub enum Conviction { Locked4x, /// 5x votes, locked for 16x... Locked5x, + /// 6x votes, locked for 32x... + Locked6x, } impl Default for Conviction { @@ -78,6 +80,7 @@ impl From for u8 { Conviction::Locked3x => 3, Conviction::Locked4x => 4, Conviction::Locked5x => 5, + Conviction::Locked6x => 6, } } } @@ -92,6 +95,7 @@ impl TryFrom for Conviction { 3 => Conviction::Locked3x, 4 => Conviction::Locked4x, 5 => Conviction::Locked5x, + 6 => Conviction::Locked6x, _ => return Err(()), }) } @@ -108,6 +112,7 @@ impl Conviction { Conviction::Locked3x => 4, Conviction::Locked4x => 8, Conviction::Locked5x => 16, + Conviction::Locked6x => 32, } } @@ -134,7 +139,7 @@ impl Bounded for Conviction { } fn max_value() -> Self { - Conviction::Locked5x + Conviction::Locked6x } } @@ -208,18 +213,19 @@ pub trait Trait: system::Trait + Sized { /// a majority-carries referendum. type ExternalMajorityOrigin: EnsureOrigin; + /// Origin from which the next tabled referendum may be forced; this allows for the tabling of + /// a negative-turnout-bias (default-carries) referendum. + type ExternalDefaultOrigin: EnsureOrigin; + /// Origin from which the next referendum proposed by the external majority may be immediately /// tabled to vote asynchronously in a similar manner to the emergency origin. It remains a /// majority-carries vote. - type ExternalPushOrigin: EnsureOrigin; - - /// Origin from which emergency referenda may be scheduled. - type EmergencyOrigin: EnsureOrigin; + type FastTrackOrigin: EnsureOrigin; - /// Minimum voting period allowed for an emergency referendum. + /// Minimum voting period allowed for an fast-track/emergency referendum. type EmergencyVotingPeriod: Get; - /// Origin from which any referenda may be cancelled in an emergency. + /// Origin from which any referendum may be cancelled in an emergency. type CancellationOrigin: EnsureOrigin; /// Origin for anyone able to veto proposals. @@ -434,32 +440,6 @@ decl_module! { Self::do_vote(who, ref_index, vote) } - /// Schedule an emergency referendum. - /// - /// This will create a new referendum for the `proposal`, approved as long as counted votes - /// exceed `threshold` and, if approved, enacted after the given `delay`. - /// - /// It may be called from either the Root or the Emergency origin. - #[weight = SimpleDispatchInfo::FixedOperational(500_000)] - fn emergency_propose(origin, - proposal: Box, - threshold: VoteThreshold, - voting_period: T::BlockNumber, - delay: T::BlockNumber - ) { - T::EmergencyOrigin::try_origin(origin) - .map(|_| ()) - .or_else(|origin| ensure_root(origin))?; - let now = >::block_number(); - // We don't consider it an error if `vote_period` is too low, but we do enforce the - // minimum. This is primarily due to practicality. If it's an emergency, we don't want - // to introduce more delays than is strictly needed by requiring a potentially costly - // resubmission in the case of a mistakenly low `vote_period`; better to just let the - // referendum take place with the lowest valid value. - let period = voting_period.max(T::EmergencyVotingPeriod::get()); - Self::inject_referendum(now + period, *proposal, threshold, delay).map(|_| ())?; - } - /// Schedule an emergency cancellation of a referendum. Cannot happen twice to the same /// referendum. #[weight = SimpleDispatchInfo::FixedOperational(500_000)] @@ -489,17 +469,26 @@ decl_module! { /// Schedule a majority-carries referendum to be tabled next once it is legal to schedule /// an external referendum. + /// + /// Unlike `external_propose`, blacklisting has no effect on this and it may replace a + /// pre-scheduled `external_propose` call. #[weight = SimpleDispatchInfo::FixedNormal(5_000_000)] fn external_propose_majority(origin, proposal: Box) { T::ExternalMajorityOrigin::ensure_origin(origin)?; - ensure!(!>::exists(), "proposal already made"); - let proposal_hash = T::Hashing::hash_of(&proposal); - if let Some((until, _)) = >::get(proposal_hash) { - ensure!(>::block_number() >= until, "proposal still blacklisted"); - } >::put((*proposal, VoteThreshold::SimpleMajority)); } + /// Schedule a negative-turnout-bias referendum to be tabled next once it is legal to + /// schedule an external referendum. + /// + /// Unlike `external_propose`, blacklisting has no effect on this and it may replace a + /// pre-scheduled `external_propose` call. + #[weight = SimpleDispatchInfo::FixedNormal(5_000_000)] + fn external_propose_default(origin, proposal: Box) { + T::ExternalDefaultOrigin::ensure_origin(origin)?; + >::put((*proposal, VoteThreshold::SuperMajorityAgainst)); + } + /// Schedule the currently externally-proposed majority-carries referendum to be tabled /// immediately. If there is no externally-proposed referendum currently, or if there is one /// but it is not a majority-carries referendum then it fails. @@ -509,14 +498,14 @@ decl_module! { /// - `delay`: The number of block after voting has ended in approval and this should be /// enacted. Increased to `EmergencyVotingPeriod` if too low. #[weight = SimpleDispatchInfo::FixedNormal(200_000)] - fn external_push(origin, + fn fast_track(origin, proposal_hash: T::Hash, voting_period: T::BlockNumber, delay: T::BlockNumber ) { - T::ExternalPushOrigin::ensure_origin(origin)?; + T::FastTrackOrigin::ensure_origin(origin)?; let (proposal, threshold) = >::get().ok_or("no proposal made")?; - ensure!(threshold == VoteThreshold::SimpleMajority, "next external proposal not simple majority"); + ensure!(threshold != VoteThreshold::SuperMajorityApprove, "next external proposal not simple majority"); ensure!(proposal_hash == T::Hashing::hash_of(&proposal), "invalid hash"); >::kill(); @@ -1028,6 +1017,7 @@ mod tests { type Origin = Origin; type Index = u64; type BlockNumber = u64; + type Call = (); type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; @@ -1090,10 +1080,10 @@ mod tests { type VotingPeriod = VotingPeriod; type EmergencyVotingPeriod = EmergencyVotingPeriod; type MinimumDeposit = MinimumDeposit; - type EmergencyOrigin = EnsureSignedBy; type ExternalOrigin = EnsureSignedBy; type ExternalMajorityOrigin = EnsureSignedBy; - type ExternalPushOrigin = EnsureSignedBy; + type ExternalDefaultOrigin = EnsureSignedBy; + type FastTrackOrigin = EnsureSignedBy; type CancellationOrigin = EnsureSignedBy; type VetoOrigin = EnsureSignedBy; type CooloffPeriod = CooloffPeriod; @@ -1347,64 +1337,6 @@ mod tests { }); } - #[test] - fn emergency_referendum_works() { - with_externalities(&mut new_test_ext(), || { - System::set_block_number(0); - assert_noop!(Democracy::emergency_propose( - Origin::signed(6), // invalid - Box::new(set_balance_proposal(2)), - VoteThreshold::SuperMajorityAgainst, - 0, - 0, - ), "bad origin: expected to be a root origin"); - assert_ok!(Democracy::emergency_propose( - Origin::signed(1), - Box::new(set_balance_proposal(2)), - VoteThreshold::SuperMajorityAgainst, - 0, - 0, - )); - assert_eq!( - Democracy::referendum_info(0), - Some(ReferendumInfo { - end: 1, - proposal: set_balance_proposal(2), - threshold: VoteThreshold::SuperMajorityAgainst, - delay: 0 - }) - ); - - assert_ok!(Democracy::vote(Origin::signed(1), 0, AYE)); - fast_forward_to(1); - assert_eq!(Balances::free_balance(&42), 0); - fast_forward_to(2); - assert_eq!(Balances::free_balance(&42), 2); - - assert_ok!(Democracy::emergency_propose( - Origin::signed(1), - Box::new(set_balance_proposal(4)), - VoteThreshold::SuperMajorityAgainst, - 3, - 3 - )); - assert_eq!( - Democracy::referendum_info(1), - Some(ReferendumInfo { - end: 5, - proposal: set_balance_proposal(4), - threshold: VoteThreshold::SuperMajorityAgainst, - delay: 3 - }) - ); - assert_ok!(Democracy::vote(Origin::signed(1), 1, AYE)); - fast_forward_to(8); - assert_eq!(Balances::free_balance(&42), 2); - fast_forward_to(9); - assert_eq!(Balances::free_balance(&42), 4); - }); - } - #[test] fn external_referendum_works() { with_externalities(&mut new_test_ext(), || { @@ -1460,17 +1392,42 @@ mod tests { } #[test] - fn external_push_referendum_works() { + fn external_default_referendum_works() { + with_externalities(&mut new_test_ext(), || { + System::set_block_number(0); + assert_noop!(Democracy::external_propose_default( + Origin::signed(3), + Box::new(set_balance_proposal(2)) + ), "Invalid origin"); + assert_ok!(Democracy::external_propose_default( + Origin::signed(1), + Box::new(set_balance_proposal(2)) + )); + fast_forward_to(1); + assert_eq!( + Democracy::referendum_info(0), + Some(ReferendumInfo { + end: 2, + proposal: set_balance_proposal(2), + threshold: VoteThreshold::SuperMajorityAgainst, + delay: 2, + }) + ); + }); + } + + #[test] + fn fast_track_referendum_works() { with_externalities(&mut new_test_ext(), || { System::set_block_number(0); let h = BlakeTwo256::hash_of(&set_balance_proposal(2)); - assert_noop!(Democracy::external_push(Origin::signed(5), h, 3, 2), "no proposal made"); + assert_noop!(Democracy::fast_track(Origin::signed(5), h, 3, 2), "no proposal made"); assert_ok!(Democracy::external_propose_majority( Origin::signed(3), Box::new(set_balance_proposal(2)) )); - assert_noop!(Democracy::external_push(Origin::signed(1), h, 3, 2), "Invalid origin"); - assert_ok!(Democracy::external_push(Origin::signed(5), h, 0, 0)); + assert_noop!(Democracy::fast_track(Origin::signed(1), h, 3, 2), "Invalid origin"); + assert_ok!(Democracy::fast_track(Origin::signed(5), h, 0, 0)); assert_eq!( Democracy::referendum_info(0), Some(ReferendumInfo { @@ -1484,7 +1441,7 @@ mod tests { } #[test] - fn external_push_referendum_fails_when_no_simple_majority() { + fn fast_track_referendum_fails_when_no_simple_majority() { with_externalities(&mut new_test_ext(), || { System::set_block_number(0); let h = BlakeTwo256::hash_of(&set_balance_proposal(2)); @@ -1493,7 +1450,7 @@ mod tests { Box::new(set_balance_proposal(2)) )); assert_noop!( - Democracy::external_push(Origin::signed(5), h, 3, 2), + Democracy::fast_track(Origin::signed(5), h, 3, 2), "next external proposal not simple majority" ); }); diff --git a/srml/elections/src/lib.rs b/srml/elections/src/lib.rs index e8fbb75dc3fc6..fe00182a89ff4 100644 --- a/srml/elections/src/lib.rs +++ b/srml/elections/src/lib.rs @@ -593,7 +593,7 @@ decl_module! { .collect(); >::put(&new_set); let new_set = new_set.into_iter().map(|x| x.0).collect::>(); - T::ChangeMembers::change_members(&[], &[who], &new_set[..]); + T::ChangeMembers::change_members(&[], &[who], new_set); } /// Set the presentation duration. If there is currently a vote being presented for, will @@ -876,7 +876,7 @@ impl Module { >::put(&new_set); let new_set = new_set.into_iter().map(|x| x.0).collect::>(); - T::ChangeMembers::change_members(&incoming, &outgoing, &new_set[..]); + T::ChangeMembers::change_members(&incoming, &outgoing, new_set); // clear all except runners-up from candidate list. let candidates = Self::candidates(); @@ -1132,6 +1132,7 @@ mod tests { type Origin = Origin; type Index = u64; type BlockNumber = u64; + type Call = (); type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; @@ -1203,7 +1204,7 @@ mod tests { pub struct TestChangeMembers; impl ChangeMembers for TestChangeMembers { - fn change_members(incoming: &[u64], outgoing: &[u64], new: &[u64]) { + fn change_members_sorted(incoming: &[u64], outgoing: &[u64], new: &[u64]) { let mut old_plus_incoming = MEMBERS.with(|m| m.borrow().to_vec()); old_plus_incoming.extend_from_slice(incoming); old_plus_incoming.sort(); diff --git a/srml/example/src/lib.rs b/srml/example/src/lib.rs index 5925c438a8a05..2d4fa43fb561e 100644 --- a/srml/example/src/lib.rs +++ b/srml/example/src/lib.rs @@ -533,6 +533,7 @@ mod tests { type Index = u64; type BlockNumber = u64; type Hash = H256; + type Call = (); type Hashing = BlakeTwo256; type AccountId = u64; type Lookup = IdentityLookup; diff --git a/srml/executive/src/lib.rs b/srml/executive/src/lib.rs index 54ea3a3f14c5e..70b4a9073d0f9 100644 --- a/srml/executive/src/lib.rs +++ b/srml/executive/src/lib.rs @@ -108,7 +108,7 @@ mod internal { fn from(d: DispatchError) -> Self { match d { DispatchError::Payment => ApplyError::CantPay, - DispatchError::Resource => ApplyError::FullBlock, + DispatchError::Exhausted => ApplyError::FullBlock, DispatchError::NoPermission => ApplyError::CantPay, DispatchError::BadState => ApplyError::CantPay, DispatchError::Stale => ApplyError::Stale, @@ -368,8 +368,7 @@ mod tests { use hex_literal::hex; impl_outer_origin! { - pub enum Origin for Runtime { - } + pub enum Origin for Runtime { } } impl_outer_event!{ @@ -390,6 +389,7 @@ mod tests { impl system::Trait for Runtime { type Origin = Origin; type Index = u64; + type Call = Call; type BlockNumber = u64; type Hash = primitives::H256; type Hashing = BlakeTwo256; diff --git a/srml/finality-tracker/src/lib.rs b/srml/finality-tracker/src/lib.rs index 4c3d975d7e58e..f074b27aa0959 100644 --- a/srml/finality-tracker/src/lib.rs +++ b/srml/finality-tracker/src/lib.rs @@ -308,6 +308,7 @@ mod tests { type Origin = Origin; type Index = u64; type BlockNumber = u64; + type Call = (); type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; diff --git a/srml/generic-asset/src/lib.rs b/srml/generic-asset/src/lib.rs index 57369b100e0e5..9120188f3b38c 100644 --- a/srml/generic-asset/src/lib.rs +++ b/srml/generic-asset/src/lib.rs @@ -1050,6 +1050,7 @@ impl PartialEq for ElevatedTrait { impl Eq for ElevatedTrait {} impl system::Trait for ElevatedTrait { type Origin = T::Origin; + type Call = T::Call; type Index = T::Index; type BlockNumber = T::BlockNumber; type Hash = T::Hash; diff --git a/srml/generic-asset/src/mock.rs b/srml/generic-asset/src/mock.rs index 80e04a6b7e429..4f8e5d886fd12 100644 --- a/srml/generic-asset/src/mock.rs +++ b/srml/generic-asset/src/mock.rs @@ -45,6 +45,7 @@ impl system::Trait for Test { type Origin = Origin; type Index = u64; type BlockNumber = u64; + type Call = (); type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; diff --git a/srml/grandpa/src/mock.rs b/srml/grandpa/src/mock.rs index 4e5d133b9ed87..055fc4673a1bc 100644 --- a/srml/grandpa/src/mock.rs +++ b/srml/grandpa/src/mock.rs @@ -51,6 +51,7 @@ impl system::Trait for Test { type Origin = Origin; type Index = u64; type BlockNumber = u64; + type Call = (); type Hash = H256; type Hashing = ::sr_primitives::traits::BlakeTwo256; type AccountId = u64; @@ -75,7 +76,9 @@ impl_outer_event!{ } pub fn to_authorities(vec: Vec<(u64, u64)>) -> Vec<(AuthorityId, u64)> { - vec.into_iter().map(|(id, weight)| (UintAuthorityId(id).into(), weight)).collect() + vec.into_iter() + .map(|(id, weight)| (UintAuthorityId(id).to_public_key::(), weight)) + .collect() } pub fn new_test_ext(authorities: Vec<(u64, u64)>) -> runtime_io::TestExternalities { diff --git a/srml/im-online/Cargo.toml b/srml/im-online/Cargo.toml index 60be1a89c2e6a..8f0e4cc39fa82 100644 --- a/srml/im-online/Cargo.toml +++ b/srml/im-online/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } sr-primitives = { path = "../../core/sr-primitives", default-features = false } primitives = { package = "substrate-primitives", path = "../../core/primitives", default-features = false } +app-crypto = { package = "substrate-application-crypto", path = "../../core/application-crypto", default-features = false } rstd = { package = "sr-std", path = "../../core/sr-std", default-features = false } serde = { version = "1.0", optional = true } session = { package = "srml-session", path = "../session", default-features = false } @@ -26,4 +27,5 @@ std = [ "srml-support/std", "sr-io/std", "system/std", + "app-crypto/std", ] diff --git a/srml/im-online/src/lib.rs b/srml/im-online/src/lib.rs index 19875d9a37cfa..23345ea9745b2 100644 --- a/srml/im-online/src/lib.rs +++ b/srml/im-online/src/lib.rs @@ -37,7 +37,6 @@ //! //! ### Public Functions //! -//! - `is_online_in_current_era` - True if the validator sent a heartbeat in the current era. //! - `is_online_in_current_session` - True if the validator sent a heartbeat in the current session. //! //! ## Usage @@ -51,9 +50,9 @@ //! //! decl_module! { //! pub struct Module for enum Call where origin: T::Origin { -//! pub fn is_online(origin, authority_id: T::AuthorityId) -> Result { +//! pub fn is_online(origin, authority_index: u32) -> Result { //! let _sender = ensure_signed(origin)?; -//! let _is_online = >::is_online_in_current_era(&authority_id); +//! let _is_online = >::is_online_in_current_session(authority_index); //! Ok(()) //! } //! } @@ -68,25 +67,39 @@ // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] -use primitives::{ - crypto::TypedKey, offchain::CryptoKey, - offchain::OpaqueNetworkState, - offchain::StorageKind, - sr25519, ed25519, -}; +use primitives::offchain::{OpaqueNetworkState, StorageKind}; use codec::{Encode, Decode}; use sr_primitives::{ - ApplyError, traits::{Member, IsMember, Extrinsic as ExtrinsicT}, + ApplyError, traits::{Extrinsic as ExtrinsicT}, transaction_validity::{TransactionValidity, TransactionLongevity, ValidTransaction}, }; use rstd::prelude::*; use session::SessionIndex; use sr_io::Printable; use srml_support::{ - Parameter, StorageValue, decl_module, decl_event, decl_storage, - traits::Get, StorageDoubleMap, print, + StorageValue, decl_module, decl_event, decl_storage, StorageDoubleMap, print, }; use system::ensure_none; +use app_crypto::RuntimeAppPublic; + +mod app { + pub use app_crypto::sr25519 as crypto; + use app_crypto::{app_crypto, key_types::IM_ONLINE, sr25519}; + + app_crypto!(sr25519, IM_ONLINE); +} + +/// A Babe authority keypair. Necessarily equivalent to the schnorrkel public key used in +/// the main Babe module. If that ever changes, then this must, too. +#[cfg(feature = "std")] +pub type AuthorityPair = app::Pair; + +/// A Babe authority signature. +pub type AuthoritySignature = app::Signature; + +/// A Babe authority identifier. Necessarily equivalent to the schnorrkel public key used in +/// the main Babe module. If that ever changes, then this must, too. +pub type AuthorityId = app::Public; // The local storage database key under which the worker progress status // is tracked. @@ -106,7 +119,6 @@ struct WorkerStatus { // Error which may occur while executing the off-chain code. enum OffchainErr { - DecodeAuthorityId, DecodeWorkerStatus, ExtrinsicCreation, FailedSigning, @@ -117,7 +129,6 @@ enum OffchainErr { impl Printable for OffchainErr { fn print(self) { match self { - OffchainErr::DecodeAuthorityId => print("Offchain error: decoding AuthorityId failed!"), OffchainErr::DecodeWorkerStatus => print("Offchain error: decoding WorkerStatus failed!"), OffchainErr::ExtrinsicCreation => print("Offchain error: extrinsic creation failed!"), OffchainErr::FailedSigning => print("Offchain error: signing failed!"), @@ -127,272 +138,236 @@ impl Printable for OffchainErr { } } -/// Heartbeat which is send/received. +pub type AuthIndex = u32; + +/// Heartbeat which is sent/received. #[derive(Encode, Decode, Clone, PartialEq, Eq)] #[cfg_attr(feature = "std", derive(Debug))] -pub struct Heartbeat +pub struct Heartbeat where BlockNumber: PartialEq + Eq + Decode + Encode, { block_number: BlockNumber, network_state: OpaqueNetworkState, - session_index: session::SessionIndex, - authority_id: AuthorityId, + session_index: SessionIndex, + authority_index: AuthIndex, } pub trait Trait: system::Trait + session::Trait { /// The overarching event type. - type Event: From> + Into<::Event>; + type Event: From + Into<::Event>; /// The function call. type Call: From>; /// A extrinsic right from the external world. This is unchecked and so /// can contain a signature. - type UncheckedExtrinsic: ExtrinsicT + Encode + Decode; - - /// The identifier type for an authority. - type AuthorityId: Member + Parameter + Default + TypedKey + Decode + Encode + AsRef<[u8]>; - - /// Number of sessions per era. - type SessionsPerEra: Get; - - /// Determine if an `AuthorityId` is a valid authority. - type IsValidAuthorityId: IsMember; + type UncheckedExtrinsic: ExtrinsicT::Call> + Encode + Decode; } decl_event!( - pub enum Event where - ::BlockNumber, - ::AuthorityId - { - /// A new heartbeat was received at this `BlockNumber` from `AuthorityId` - HeartbeatReceived(BlockNumber, AuthorityId), + pub enum Event { + /// A new heartbeat was received from `AuthorityId` + HeartbeatReceived(AuthorityId), } ); decl_storage! { trait Store for Module as ImOnline { - // The block number when we should gossip. + /// The block number when we should gossip. GossipAt get(gossip_at) config(): T::BlockNumber; - // The session index when the last new era started. - LastNewEraStart get(last_new_era_start) config(): Option; + /// The current set of keys that may issue a heartbeat. + Keys get(keys) config(): Vec; - // For each session index we keep a mapping of `AuthorityId` to - // `offchain::OpaqueNetworkState`. - ReceivedHeartbeats get(received_heartbeats): double_map session::SessionIndex, - blake2_256(T::AuthorityId) => Vec; + /// For each session index we keep a mapping of `AuthorityId` + /// to `offchain::OpaqueNetworkState`. + ReceivedHeartbeats get(received_heartbeats): double_map SessionIndex, + blake2_256(AuthIndex) => Vec; } } + decl_module! { pub struct Module for enum Call where origin: T::Origin { - /// Number of sessions per era. - const SessionsPerEra: session::SessionIndex = T::SessionsPerEra::get(); - - fn deposit_event() = default; + fn deposit_event() = default; fn heartbeat( origin, - heartbeat: Heartbeat, - _signature: Vec + heartbeat: Heartbeat, + _signature: AuthoritySignature ) { ensure_none(origin)?; let current_session = >::current_index(); - let exists = >::exists(¤t_session, &heartbeat.authority_id); - if !exists { - let now = >::block_number(); - Self::deposit_event(RawEvent::HeartbeatReceived(now, heartbeat.authority_id.clone())); + let exists = ::exists( + ¤t_session, + &heartbeat.authority_index + ); + let keys = Keys::get(); + let public = keys.get(heartbeat.authority_index as usize); + if let (true, Some(public)) = (!exists, public) { + Self::deposit_event(Event::HeartbeatReceived(public.clone())); let network_state = heartbeat.network_state.encode(); - >::insert(¤t_session, &heartbeat.authority_id, &network_state); + ::insert( + ¤t_session, + &heartbeat.authority_index, + &network_state + ); } } // Runs after every block. fn offchain_worker(now: T::BlockNumber) { - fn gossip_at(block_number: T::BlockNumber) -> Result<(), OffchainErr> { - // we run only when a local authority key is configured - if let Ok(key) = sr_io::pubkey(CryptoKey::AuthorityKey) { - let authority_id = ::AuthorityId::decode(&mut &key[..]) - .map_err(|_| OffchainErr::DecodeAuthorityId)?; - let network_state = - sr_io::network_state().map_err(|_| OffchainErr::NetworkState)?; - let heartbeat_data = Heartbeat { - block_number, - network_state, - session_index: >::current_index(), - authority_id, - }; - - let signature = sr_io::sign(CryptoKey::AuthorityKey, &heartbeat_data.encode()) - .map_err(|_| OffchainErr::FailedSigning)?; - let call = Call::heartbeat(heartbeat_data, signature); - let ex = T::UncheckedExtrinsic::new_unsigned(call.into()) - .ok_or(OffchainErr::ExtrinsicCreation)?; - sr_io::submit_transaction(&ex) - .map_err(|_| OffchainErr::SubmitTransaction)?; - - // once finished we set the worker status without comparing - // if the existing value changed in the meantime. this is - // because at this point the heartbeat was definitely submitted. - set_worker_status::(block_number, true); - } - Ok(()) - } - - fn compare_and_set_worker_status( - gossipping_at: T::BlockNumber, - done: bool, - curr_worker_status: Option>, - ) -> bool { - let enc = WorkerStatus { - done, - gossipping_at, - }; - sr_io::local_storage_compare_and_set( - StorageKind::PERSISTENT, - DB_KEY, - curr_worker_status.as_ref().map(Vec::as_slice), - &enc.encode() - ) - } - - fn set_worker_status( - gossipping_at: T::BlockNumber, - done: bool, - ) { - let enc = WorkerStatus { - done, - gossipping_at, - }; - sr_io::local_storage_set( - StorageKind::PERSISTENT, DB_KEY, &enc.encode()); - } - - // Checks if a heartbeat gossip already occurred at this block number. - // Returns a tuple of `(current worker status, bool)`, whereby the bool - // is true if not yet gossipped. - fn check_not_yet_gossipped( - now: T::BlockNumber, - next_gossip: T::BlockNumber, - ) -> Result<(Option>, bool), OffchainErr> { - let last_gossip = sr_io::local_storage_get(StorageKind::PERSISTENT, DB_KEY); - match last_gossip { - Some(last) => { - let worker_status: WorkerStatus = Decode::decode(&mut &last[..]) - .map_err(|_| OffchainErr::DecodeWorkerStatus)?; - - let was_aborted = !worker_status.done && worker_status.gossipping_at < now; - - // another off-chain worker is currently in the process of submitting - let already_submitting = - !worker_status.done && worker_status.gossipping_at == now; - - let not_yet_gossipped = - worker_status.done && worker_status.gossipping_at < next_gossip; - - let ret = (was_aborted && !already_submitting) || not_yet_gossipped; - Ok((Some(last), ret)) - }, - None => Ok((None, true)), - } - } - - let next_gossip = >::get(); - let check = check_not_yet_gossipped::(now, next_gossip); - let (curr_worker_status, not_yet_gossipped) = match check { - Ok((s, v)) => (s, v), - Err(err) => { - print(err); - return; - }, - }; - if next_gossip < now && not_yet_gossipped { - let value_set = compare_and_set_worker_status::(now, false, curr_worker_status); - if !value_set { - // value could not be set in local storage, since the value was - // different from `curr_worker_status`. this indicates that - // another worker was running in parallel. - return; - } - - match gossip_at::(now) { - Ok(_) => {}, - Err(err) => print(err), - } - } + Self::offchain(now); } } } impl Module { - /// Returns `true` if a heartbeat has been received for `AuthorityId` - /// during the current era. Otherwise `false`. - pub fn is_online_in_current_era(authority_id: &T::AuthorityId) -> bool { - let curr = >::current_index(); - match LastNewEraStart::get() { - Some(start) => { - // iterate over every session - for index in start..curr { - if >::exists(&index, authority_id) { - return true; - } - } - false + /// Returns `true` if a heartbeat has been received for the authority at `authority_index` in + /// the authorities series, during the current session. Otherwise `false`. + pub fn is_online_in_current_session(authority_index: AuthIndex) -> bool { + let current_session = >::current_index(); + ::exists(¤t_session, &authority_index) + } + + fn offchain(now: T::BlockNumber) { + let next_gossip = >::get(); + let check = Self::check_not_yet_gossipped(now, next_gossip); + let (curr_worker_status, not_yet_gossipped) = match check { + Ok((s, v)) => (s, v), + Err(err) => { + print(err); + return; }, - None => >::exists(&curr, authority_id), + }; + if next_gossip < now && not_yet_gossipped { + let value_set = Self::compare_and_set_worker_status(now, false, curr_worker_status); + if !value_set { + // value could not be set in local storage, since the value was + // different from `curr_worker_status`. this indicates that + // another worker was running in parallel. + return; + } + + match Self::do_gossip_at(now) { + Ok(_) => {}, + Err(err) => print(err), + } } } - /// Returns `true` if a heartbeat has been received for `AuthorityId` - /// during the current session. Otherwise `false`. - pub fn is_online_in_current_session(authority_id: &T::AuthorityId) -> bool { - let current_session = >::current_index(); - >::exists(¤t_session, authority_id) - } + fn do_gossip_at(block_number: T::BlockNumber) -> Result<(), OffchainErr> { + // we run only when a local authority key is configured + let authorities = Keys::get(); + let mut local_keys = app::Public::all(); + local_keys.sort(); + + for (authority_index, key) in authorities.into_iter() + .enumerate() + .filter_map(|(index, authority)| { + local_keys.binary_search(&authority) + .ok() + .map(|location| (index as u32, &local_keys[location])) + }) + { + let network_state = sr_io::network_state().map_err(|_| OffchainErr::NetworkState)?; + let heartbeat_data = Heartbeat { + block_number, + network_state, + session_index: >::current_index(), + authority_index, + }; - /// Session has just changed. - fn new_session() { - let now = >::block_number(); - >::put(now); + let signature = key.sign(&heartbeat_data.encode()).ok_or(OffchainErr::FailedSigning)?; + let call = Call::heartbeat(heartbeat_data, signature); + let ex = T::UncheckedExtrinsic::new_unsigned(call.into()) + .ok_or(OffchainErr::ExtrinsicCreation)?; + sr_io::submit_transaction(&ex).map_err(|_| OffchainErr::SubmitTransaction)?; - let current_session = >::current_index(); + // once finished we set the worker status without comparing + // if the existing value changed in the meantime. this is + // because at this point the heartbeat was definitely submitted. + Self::set_worker_status(block_number, true); + } + Ok(()) + } - match LastNewEraStart::get() { - Some(last_new_era_start) => { - let sessions_per_era = T::SessionsPerEra::get(); + fn compare_and_set_worker_status( + gossipping_at: T::BlockNumber, + done: bool, + curr_worker_status: Option>, + ) -> bool { + let enc = WorkerStatus { + done, + gossipping_at, + }; + sr_io::local_storage_compare_and_set( + StorageKind::PERSISTENT, + DB_KEY, + curr_worker_status.as_ref().map(Vec::as_slice), + &enc.encode() + ) + } - let new_era = current_session - last_new_era_start > sessions_per_era; - if new_era { - LastNewEraStart::put(current_session); - Self::remove_heartbeats(); - } - }, - None => LastNewEraStart::put(current_session), + fn set_worker_status( + gossipping_at: T::BlockNumber, + done: bool, + ) { + let enc = WorkerStatus { + done, + gossipping_at, }; + sr_io::local_storage_set( + StorageKind::PERSISTENT, DB_KEY, &enc.encode()); } - // Remove all stored heartbeats. - fn remove_heartbeats() { - let curr = >::current_index(); - match LastNewEraStart::get() { - Some(start) => { - for index in start..curr { - >::remove_prefix(&index); - } + // Checks if a heartbeat gossip already occurred at this block number. + // Returns a tuple of `(current worker status, bool)`, whereby the bool + // is true if not yet gossipped. + fn check_not_yet_gossipped( + now: T::BlockNumber, + next_gossip: T::BlockNumber, + ) -> Result<(Option>, bool), OffchainErr> { + let last_gossip = sr_io::local_storage_get(StorageKind::PERSISTENT, DB_KEY); + match last_gossip { + Some(last) => { + let worker_status: WorkerStatus = Decode::decode(&mut &last[..]) + .map_err(|_| OffchainErr::DecodeWorkerStatus)?; + + let was_aborted = !worker_status.done && worker_status.gossipping_at < now; + + // another off-chain worker is currently in the process of submitting + let already_submitting = + !worker_status.done && worker_status.gossipping_at == now; + + let not_yet_gossipped = + worker_status.done && worker_status.gossipping_at < next_gossip; + + let ret = (was_aborted && !already_submitting) || not_yet_gossipped; + Ok((Some(last), ret)) }, - None => >::remove_prefix(&curr), + None => Ok((None, true)), } } + } impl session::OneSessionHandler for Module { - type Key = ::AuthorityId; + type Key = AuthorityId; + + fn on_new_session<'a, I: 'a>(_changed: bool, _validators: I, next_validators: I) + where I: Iterator + { + // Reset heartbeats + ::remove_prefix(&>::current_index()); + + // Tell the offchain worker to start making the next session's heartbeats. + >::put(>::block_number()); - fn on_new_session<'a, I: 'a>(_changed: bool, _validators: I, _next_validators: I) { - Self::new_session(); + // Remember who the authorities are for the new session. + Keys::put(next_validators.map(|x| x.1).collect::>()); } fn on_disabled(_i: usize) { @@ -405,55 +380,42 @@ impl srml_support::unsigned::ValidateUnsigned for Module { fn validate_unsigned(call: &Self::Call) -> srml_support::unsigned::TransactionValidity { if let Call::heartbeat(heartbeat, signature) = call { - // verify that the incoming (unverified) pubkey is actually an authority id - let is_authority = T::IsValidAuthorityId::is_member(&heartbeat.authority_id); - if !is_authority { - return TransactionValidity::Invalid(ApplyError::BadSignature as i8); - } - - if >::is_online_in_current_session(&heartbeat.authority_id) { + if >::is_online_in_current_session(heartbeat.authority_index) { // we already received a heartbeat for this authority - return TransactionValidity::Invalid(ApplyError::BadSignature as i8); + return TransactionValidity::Invalid(ApplyError::Stale as i8); } - if signature.len() != 64 { - return TransactionValidity::Invalid(ApplyError::BadSignature as i8); + // check if session index from heartbeat is recent + let current_session = >::current_index(); + if heartbeat.session_index != current_session { + return TransactionValidity::Invalid(ApplyError::Stale as i8); } - let signature = { - let mut array = [0; 64]; - array.copy_from_slice(&signature); // panics if not enough, hence the check above - array + // verify that the incoming (unverified) pubkey is actually an authority id + let keys = Keys::get(); + let authority_id = match keys.get(heartbeat.authority_index as usize) { + Some(id) => id, + None => return TransactionValidity::Invalid(ApplyError::BadSignature as i8), }; - let encoded_heartbeat = heartbeat.encode(); - - let signature_valid = match ::KEY_TYPE { - ed25519::Public::KEY_TYPE => - sr_io::ed25519_verify(&signature, &encoded_heartbeat, &heartbeat.authority_id), - sr25519::Public::KEY_TYPE => - sr_io::sr25519_verify(&signature, &encoded_heartbeat, &heartbeat.authority_id), - _ => return TransactionValidity::Invalid(ApplyError::BadSignature as i8), - }; + // check signature (this is expensive so we do it last). + let signature_valid = heartbeat.using_encoded(|encoded_heartbeat| { + authority_id.verify(&encoded_heartbeat, &signature) + }); if !signature_valid { return TransactionValidity::Invalid(ApplyError::BadSignature as i8); } - // check if session index from heartbeat is recent - let current_session = >::current_index(); - if heartbeat.session_index < current_session { - return TransactionValidity::Invalid(ApplyError::BadSignature as i8); - } - return TransactionValidity::Valid(ValidTransaction { priority: 0, requires: vec![], - provides: vec![encoded_heartbeat], + provides: vec![(current_session, authority_id).encode()], longevity: TransactionLongevity::max_value(), propagate: true, }) } + TransactionValidity::Invalid(0) } } diff --git a/srml/indices/src/mock.rs b/srml/indices/src/mock.rs index ef4b2eb649a01..d3ddfb0e09f7f 100644 --- a/srml/indices/src/mock.rs +++ b/srml/indices/src/mock.rs @@ -75,6 +75,7 @@ impl system::Trait for Runtime { type Origin = Origin; type Index = u64; type BlockNumber = u64; + type Call = (); type Hash = H256; type Hashing = ::sr_primitives::traits::BlakeTwo256; type AccountId = u64; diff --git a/srml/membership/Cargo.toml b/srml/membership/Cargo.toml new file mode 100644 index 0000000000000..5126f41f8929a --- /dev/null +++ b/srml/membership/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "srml-membership" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +serde = { version = "1.0", optional = true } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false } +sr-std = { path = "../../core/sr-std", default-features = false } +sr-io = { path = "../../core/sr-io", default-features = false } +srml-support = { path = "../support", default-features = false } +system = { package = "srml-system", path = "../system", default-features = false } +sr-primitives = { path = "../../core/sr-primitives", default-features = false } + +[dev-dependencies] +primitives = { package = "substrate-primitives", path = "../../core/primitives" } + +[features] +default = ["std"] +std = [ + "serde", + "codec/std", + "sr-primitives/std", + "sr-std/std", + "sr-io/std", + "srml-support/std", + "system/std", +] diff --git a/srml/membership/src/lib.rs b/srml/membership/src/lib.rs new file mode 100644 index 0000000000000..805ec368bbad9 --- /dev/null +++ b/srml/membership/src/lib.rs @@ -0,0 +1,347 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! # Membership Module +//! +//! Allows control of membership of a set of `AccountId`s, useful for managing membership of of a +//! collective. + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +use sr_std::prelude::*; +use srml_support::{ + StorageValue, decl_module, decl_storage, decl_event, + traits::{ChangeMembers} +}; +use system::ensure_root; +use sr_primitives::{traits::EnsureOrigin, weights::SimpleDispatchInfo}; + +pub trait Trait: system::Trait { + /// The overarching event type. + type Event: From> + Into<::Event>; + + /// Required origin for adding a member (though can always be Root). + type AddOrigin: EnsureOrigin; + + /// Required origin for removing a member (though can always be Root). + type RemoveOrigin: EnsureOrigin; + + /// Required origin for adding and removing a member in a single action. + type SwapOrigin: EnsureOrigin; + + /// Required origin for resetting membership. + type ResetOrigin: EnsureOrigin; + + /// The receiver of the signal for when the membership has been initialized. This happens pre- + /// genesis and will usually be the same as `MembershipChanged`. If you need to do something + /// different on initialization, then you can change this accordingly. + type MembershipInitialized: ChangeMembers; + + /// The receiver of the signal for when the membership has changed. + type MembershipChanged: ChangeMembers; +} + +decl_storage! { + trait Store for Module, I: Instance=DefaultInstance> as Membership { + /// The current membership, stored as an ordered Vec. + Members get(members): Vec; + } + add_extra_genesis { + config(members): Vec; + config(phantom): sr_std::marker::PhantomData; + build(| + storage: &mut sr_primitives::StorageOverlay, + _: &mut sr_primitives::ChildrenStorageOverlay, + config: &GenesisConfig + | { + sr_io::with_storage(storage, || { + let mut members = config.members.clone(); + members.sort(); + T::MembershipInitialized::set_members_sorted(&members[..], &[]); + >::put(members); + }); + }) + } +} + +decl_event!( + pub enum Event where + ::AccountId, + { + /// The given member was added; see the transaction for who. + MemberAdded, + /// The given member was removed; see the transaction for who. + MemberRemoved, + /// Two members were swapped; see the transaction for who. + MembersSwapped, + /// The membership was reset; see the transaction for who the new set is. + MembersReset, + /// Phantom member, never used. + Dummy(sr_std::marker::PhantomData<(AccountId, I)>), + } +); + +decl_module! { + pub struct Module, I: Instance=DefaultInstance> + for enum Call + where origin: T::Origin + { + fn deposit_event() = default; + + /// Add a member `who` to the set. + /// + /// May only be called from `AddOrigin` or root. + #[weight = SimpleDispatchInfo::FixedNormal(50_000)] + fn add_member(origin, who: T::AccountId) { + T::AddOrigin::try_origin(origin) + .map(|_| ()) + .or_else(ensure_root) + .map_err(|_| "bad origin")?; + + let mut members = >::get(); + let location = members.binary_search(&who).err().ok_or("already a member")?; + members.insert(location, who.clone()); + >::put(&members); + + T::MembershipChanged::change_members_sorted(&[who], &[], &members[..]); + + Self::deposit_event(RawEvent::MemberAdded); + } + + /// Remove a member `who` from the set. + /// + /// May only be called from `RemoveOrigin` or root. + #[weight = SimpleDispatchInfo::FixedNormal(50_000)] + fn remove_member(origin, who: T::AccountId) { + T::RemoveOrigin::try_origin(origin) + .map(|_| ()) + .or_else(ensure_root) + .map_err(|_| "bad origin")?; + + let mut members = >::get(); + let location = members.binary_search(&who).ok().ok_or("not a member")?; + members.remove(location); + >::put(&members); + + T::MembershipChanged::change_members_sorted(&[], &[who], &members[..]); + + Self::deposit_event(RawEvent::MemberRemoved); + } + + /// Swap out one member `remove` for another `add`. + /// + /// May only be called from `SwapOrigin` or root. + #[weight = SimpleDispatchInfo::FixedNormal(50_000)] + fn swap_member(origin, remove: T::AccountId, add: T::AccountId) { + T::SwapOrigin::try_origin(origin) + .map(|_| ()) + .or_else(ensure_root) + .map_err(|_| "bad origin")?; + + if remove == add { return Ok(()) } + + let mut members = >::get(); + let location = members.binary_search(&remove).ok().ok_or("not a member")?; + members[location] = add.clone(); + let _location = members.binary_search(&add).err().ok_or("already a member")?; + members.sort(); + >::put(&members); + + T::MembershipChanged::change_members_sorted( + &[add], + &[remove], + &members[..], + ); + + Self::deposit_event(RawEvent::MembersSwapped); + } + + /// Change the membership to a new set, disregarding the existing membership. Be nice and + /// pass `members` pre-sorted. + /// + /// May only be called from `ResetOrigin` or root. + #[weight = SimpleDispatchInfo::FixedNormal(50_000)] + fn reset_members(origin, members: Vec) { + T::ResetOrigin::try_origin(origin) + .map(|_| ()) + .or_else(ensure_root) + .map_err(|_| "bad origin")?; + + let mut members = members; + members.sort(); + >::mutate(|m| { + T::MembershipChanged::set_members_sorted(&members[..], m); + *m = members; + }); + + Self::deposit_event(RawEvent::MembersReset); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use std::cell::RefCell; + use srml_support::{assert_ok, assert_noop, impl_outer_origin, parameter_types}; + use sr_io::with_externalities; + use primitives::{H256, Blake2Hasher}; + // The testing primitives are very useful for avoiding having to work with signatures + // or public keys. `u64` is used as the `AccountId` and no `Signature`s are requried. + use sr_primitives::{ + Perbill, traits::{BlakeTwo256, IdentityLookup}, testing::Header + }; + use system::EnsureSignedBy; + + impl_outer_origin! { + pub enum Origin for Test {} + } + + // For testing the module, we construct most of a mock runtime. This means + // first constructing a configuration type (`Test`) which `impl`s each of the + // configuration traits of modules we want to use. + #[derive(Clone, Eq, PartialEq)] + pub struct Test; + parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + } + impl system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Call = (); + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type WeightMultiplierUpdate = (); + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + } + parameter_types! { + pub const One: u64 = 1; + pub const Two: u64 = 2; + pub const Three: u64 = 3; + pub const Four: u64 = 4; + pub const Five: u64 = 5; + } + + thread_local! { + static MEMBERS: RefCell> = RefCell::new(vec![]); + } + + pub struct TestChangeMembers; + impl ChangeMembers for TestChangeMembers { + fn change_members_sorted(incoming: &[u64], outgoing: &[u64], new: &[u64]) { + let mut old_plus_incoming = MEMBERS.with(|m| m.borrow().to_vec()); + old_plus_incoming.extend_from_slice(incoming); + old_plus_incoming.sort(); + let mut new_plus_outgoing = new.to_vec(); + new_plus_outgoing.extend_from_slice(outgoing); + new_plus_outgoing.sort(); + assert_eq!(old_plus_incoming, new_plus_outgoing); + + MEMBERS.with(|m| *m.borrow_mut() = new.to_vec()); + } + } + + impl Trait for Test { + type Event = (); + type AddOrigin = EnsureSignedBy; + type RemoveOrigin = EnsureSignedBy; + type SwapOrigin = EnsureSignedBy; + type ResetOrigin = EnsureSignedBy; + type MembershipInitialized = TestChangeMembers; + type MembershipChanged = TestChangeMembers; + } + + type Membership = Module; + + // This function basically just builds a genesis storage key/value store according to + // our desired mockup. + fn new_test_ext() -> sr_io::TestExternalities { + let mut t = system::GenesisConfig::default().build_storage::().unwrap().0; + // We use default for brevity, but you can configure as desired if needed. + t.extend(GenesisConfig::{ + members: vec![10, 20, 30], + .. Default::default() + }.build_storage().unwrap().0); + t.into() + } + + #[test] + fn query_membership_works() { + with_externalities(&mut new_test_ext(), || { + assert_eq!(Membership::members(), vec![10, 20, 30]); + assert_eq!(MEMBERS.with(|m| m.borrow().clone()), vec![10, 20, 30]); + }); + } + + #[test] + fn add_member_works() { + with_externalities(&mut new_test_ext(), || { + assert_noop!(Membership::add_member(Origin::signed(5), 15), "bad origin"); + assert_noop!(Membership::add_member(Origin::signed(1), 10), "already a member"); + assert_ok!(Membership::add_member(Origin::signed(1), 15)); + assert_eq!(Membership::members(), vec![10, 15, 20, 30]); + assert_eq!(MEMBERS.with(|m| m.borrow().clone()), Membership::members()); + }); + } + + #[test] + fn remove_member_works() { + with_externalities(&mut new_test_ext(), || { + assert_noop!(Membership::remove_member(Origin::signed(5), 20), "bad origin"); + assert_noop!(Membership::remove_member(Origin::signed(2), 15), "not a member"); + assert_ok!(Membership::remove_member(Origin::signed(2), 20)); + assert_eq!(Membership::members(), vec![10, 30]); + assert_eq!(MEMBERS.with(|m| m.borrow().clone()), Membership::members()); + }); + } + + #[test] + fn swap_member_works() { + with_externalities(&mut new_test_ext(), || { + assert_noop!(Membership::swap_member(Origin::signed(5), 10, 25), "bad origin"); + assert_noop!(Membership::swap_member(Origin::signed(3), 15, 25), "not a member"); + assert_noop!(Membership::swap_member(Origin::signed(3), 10, 30), "already a member"); + assert_ok!(Membership::swap_member(Origin::signed(3), 20, 20)); + assert_eq!(Membership::members(), vec![10, 20, 30]); + assert_ok!(Membership::swap_member(Origin::signed(3), 10, 25)); + assert_eq!(Membership::members(), vec![20, 25, 30]); + assert_eq!(MEMBERS.with(|m| m.borrow().clone()), Membership::members()); + }); + } + + #[test] + fn reset_members_works() { + with_externalities(&mut new_test_ext(), || { + assert_noop!(Membership::reset_members(Origin::signed(1), vec![20, 40, 30]), "bad origin"); + assert_ok!(Membership::reset_members(Origin::signed(4), vec![20, 40, 30])); + assert_eq!(Membership::members(), vec![20, 30, 40]); + assert_eq!(MEMBERS.with(|m| m.borrow().clone()), Membership::members()); + }); + } +} diff --git a/srml/session/Cargo.toml b/srml/session/Cargo.toml index 66d47f4c3585f..d084ab4261b3c 100644 --- a/srml/session/Cargo.toml +++ b/srml/session/Cargo.toml @@ -18,6 +18,7 @@ runtime_io = { package = "sr-io", path = "../../core/sr-io", default-features = [dev-dependencies] primitives = { package = "substrate-primitives", path = "../../core/primitives" } +app-crypto = { package = "substrate-application-crypto", path = "../../core/application-crypto" } lazy_static = "1.0" [features] diff --git a/srml/session/src/historical.rs b/srml/session/src/historical.rs index e35df3011c1d9..815d782cf902d 100644 --- a/srml/session/src/historical.rs +++ b/srml/session/src/historical.rs @@ -100,9 +100,9 @@ impl Module { /// Specialization of the crate-level `OnSessionEnding` which returns the old /// set of full identification when changing the validator set. pub trait OnSessionEnding: crate::OnSessionEnding { - /// Returns the set of new validators, if any, along with the old validators - /// and their full identifications. - fn on_session_ending(ending: SessionIndex, applied_at: SessionIndex) + /// If there was a validator set change, its returns the set of new validators along with the + /// old validators and their full identifications. + fn on_session_ending(ending: SessionIndex, will_apply_at: SessionIndex) -> Option<(Vec, Vec<(ValidatorId, FullIdentification)>)>; } @@ -312,11 +312,8 @@ impl> srml_support::traits::KeyOwnerProofSystem<(KeyTyp mod tests { use super::*; use runtime_io::with_externalities; - use primitives::Blake2Hasher; - use sr_primitives::{ - traits::OnInitialize, - testing::{UintAuthorityId, UINT_DUMMY_KEY}, - }; + use primitives::{Blake2Hasher, crypto::key_types::DUMMY}; + use sr_primitives::{traits::OnInitialize, testing::UintAuthorityId}; use crate::mock::{ NEXT_VALIDATORS, force_new_session, set_next_validators, Test, System, Session, @@ -329,7 +326,7 @@ mod tests { let mut t = system::GenesisConfig::default().build_storage::().unwrap().0; let (storage, _child_storage) = crate::GenesisConfig:: { keys: NEXT_VALIDATORS.with(|l| - l.borrow().iter().cloned().map(|i| (i, UintAuthorityId(i))).collect() + l.borrow().iter().cloned().map(|i| (i, UintAuthorityId(i).into())).collect() ), }.build_storage().unwrap(); t.extend(storage); @@ -346,15 +343,10 @@ mod tests { Session::on_initialize(1); let encoded_key_1 = UintAuthorityId(1).encode(); - let proof = Historical::prove((UINT_DUMMY_KEY, &encoded_key_1[..])).unwrap(); + let proof = Historical::prove((DUMMY, &encoded_key_1[..])).unwrap(); // proof-checking in the same session is OK. - assert!( - Historical::check_proof( - (UINT_DUMMY_KEY, &encoded_key_1[..]), - proof.clone(), - ).is_some() - ); + assert!(Historical::check_proof((DUMMY, &encoded_key_1[..]), proof.clone()).is_some()); set_next_validators(vec![1, 2, 4]); force_new_session(); @@ -370,12 +362,7 @@ mod tests { assert!(Session::current_index() > proof.session); // proof-checking in the next session is also OK. - assert!( - Historical::check_proof( - (UINT_DUMMY_KEY, &encoded_key_1[..]), - proof.clone(), - ).is_some() - ); + assert!(Historical::check_proof((DUMMY, &encoded_key_1[..]), proof.clone()).is_some()); set_next_validators(vec![1, 2, 5]); diff --git a/srml/session/src/lib.rs b/srml/session/src/lib.rs index 8b9e8b156636d..3aded46ea0223 100644 --- a/srml/session/src/lib.rs +++ b/srml/session/src/lib.rs @@ -121,9 +121,9 @@ use rstd::{prelude::*, marker::PhantomData, ops::{Sub, Rem}}; use codec::Decode; -use sr_primitives::KeyTypeId; +use sr_primitives::{KeyTypeId, AppKey}; use sr_primitives::weights::SimpleDispatchInfo; -use sr_primitives::traits::{Convert, Zero, Member, OpaqueKeys, TypedKey}; +use sr_primitives::traits::{Convert, Zero, Member, OpaqueKeys}; use srml_support::{ dispatch::Result, ConsensusEngineId, StorageValue, StorageDoubleMap, for_each_tuple, decl_module, decl_event, decl_storage, @@ -172,10 +172,13 @@ pub trait OnSessionEnding { /// Handle the fact that the session is ending, and optionally provide the new validator set. /// /// `ending_index` is the index of the currently ending session. - /// The returned validator set, if any, will not be applied until `next_index`. - /// `next_index` is guaranteed to be at least `ending_index + 1`, since session indices don't - /// repeat. - fn on_session_ending(ending_index: SessionIndex, next_index: SessionIndex) -> Option>; + /// The returned validator set, if any, will not be applied until `will_apply_at`. + /// `will_apply_at` is guaranteed to be at least `ending_index + 1`, since session indices don't + /// repeat, but it could be some time after in case we are staging authority set changes. + fn on_session_ending( + ending_index: SessionIndex, + will_apply_at: SessionIndex + ) -> Option>; } impl OnSessionEnding for () { @@ -198,7 +201,7 @@ pub trait SessionHandler { /// One session-key type handler. pub trait OneSessionHandler { /// The key type expected. - type Key: Decode + Default + TypedKey; + type Key: Decode + Default + AppKey; fn on_new_session<'a, I: 'a>(changed: bool, validators: I, queued_validators: I) where I: Iterator, ValidatorId: 'a; @@ -222,10 +225,10 @@ macro_rules! impl_session_handlers { ) { $( let our_keys: Box> = Box::new(validators.iter() - .map(|k| (&k.0, k.1.get::<$t::Key>(<$t::Key as TypedKey>::KEY_TYPE) + .map(|k| (&k.0, k.1.get::<$t::Key>(<$t::Key as AppKey>::ID) .unwrap_or_default()))); let queued_keys: Box> = Box::new(queued_validators.iter() - .map(|k| (&k.0, k.1.get::<$t::Key>(<$t::Key as TypedKey>::KEY_TYPE) + .map(|k| (&k.0, k.1.get::<$t::Key>(<$t::Key as AppKey>::ID) .unwrap_or_default()))); $t::on_new_session(changed, our_keys, queued_keys); )* @@ -562,7 +565,7 @@ mod tests { use super::*; use srml_support::assert_ok; use runtime_io::with_externalities; - use primitives::Blake2Hasher; + use primitives::{Blake2Hasher, crypto::key_types::DUMMY}; use sr_primitives::{ traits::OnInitialize, testing::UintAuthorityId, @@ -576,7 +579,7 @@ mod tests { let mut t = system::GenesisConfig::default().build_storage::().unwrap(); GenesisConfig:: { keys: NEXT_VALIDATORS.with(|l| - l.borrow().iter().cloned().map(|i| (i, UintAuthorityId(i))).collect() + l.borrow().iter().cloned().map(|i| (i, UintAuthorityId(i).into())).collect() ), }.assimilate_storage(&mut t.0, &mut t.1).unwrap(); runtime_io::TestExternalities::new_with_children(t) @@ -599,8 +602,8 @@ mod tests { #[test] fn put_get_keys() { with_externalities(&mut new_test_ext(), || { - Session::put_keys(&10, &UintAuthorityId(10)); - assert_eq!(Session::load_keys(&10), Some(UintAuthorityId(10))); + Session::put_keys(&10, &UintAuthorityId(10).into()); + assert_eq!(Session::load_keys(&10), Some(UintAuthorityId(10).into())); }) } @@ -609,9 +612,9 @@ mod tests { let mut ext = new_test_ext(); with_externalities(&mut ext, || { assert_eq!(Session::validators(), vec![1, 2, 3]); - assert_eq!(Session::load_keys(&1), Some(UintAuthorityId(1))); + assert_eq!(Session::load_keys(&1), Some(UintAuthorityId(1).into())); - let id = ::KEY_TYPE; + let id = DUMMY; assert_eq!(Session::key_owner(id, UintAuthorityId(1).get_raw(id)), Some(1)); Session::on_free_balance_zero(&1); @@ -629,8 +632,8 @@ mod tests { force_new_session(); initialize_block(1); assert_eq!(Session::queued_keys(), vec![ - (1, UintAuthorityId(1)), - (2, UintAuthorityId(2)), + (1, UintAuthorityId(1).into()), + (2, UintAuthorityId(2).into()), ]); assert_eq!(Session::validators(), vec![1, 2, 3]); assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]); @@ -638,20 +641,20 @@ mod tests { force_new_session(); initialize_block(2); assert_eq!(Session::queued_keys(), vec![ - (1, UintAuthorityId(1)), - (2, UintAuthorityId(2)), + (1, UintAuthorityId(1).into()), + (2, UintAuthorityId(2).into()), ]); assert_eq!(Session::validators(), vec![1, 2]); assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2)]); set_next_validators(vec![1, 2, 4]); - assert_ok!(Session::set_keys(Origin::signed(4), UintAuthorityId(4), vec![])); + assert_ok!(Session::set_keys(Origin::signed(4), UintAuthorityId(4).into(), vec![])); force_new_session(); initialize_block(3); assert_eq!(Session::queued_keys(), vec![ - (1, UintAuthorityId(1)), - (2, UintAuthorityId(2)), - (4, UintAuthorityId(4)), + (1, UintAuthorityId(1).into()), + (2, UintAuthorityId(2).into()), + (4, UintAuthorityId(4).into()), ]); assert_eq!(Session::validators(), vec![1, 2]); assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2)]); @@ -659,9 +662,9 @@ mod tests { force_new_session(); initialize_block(4); assert_eq!(Session::queued_keys(), vec![ - (1, UintAuthorityId(1)), - (2, UintAuthorityId(2)), - (4, UintAuthorityId(4)), + (1, UintAuthorityId(1).into()), + (2, UintAuthorityId(2).into()), + (4, UintAuthorityId(4).into()), ]); assert_eq!(Session::validators(), vec![1, 2, 4]); assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(4)]); @@ -704,7 +707,7 @@ mod tests { // Block 3: Set new key for validator 2; no visible change. initialize_block(3); - assert_ok!(Session::set_keys(Origin::signed(2), UintAuthorityId(5), vec![])); + assert_ok!(Session::set_keys(Origin::signed(2), UintAuthorityId(5).into(), vec![])); assert_eq!(authorities(), vec![UintAuthorityId(1), UintAuthorityId(2), UintAuthorityId(3)]); // Block 4: Session rollover; no visible change. @@ -726,11 +729,11 @@ mod tests { with_externalities(&mut new_test_ext(), || { System::set_block_number(1); Session::on_initialize(1); - assert!(Session::set_keys(Origin::signed(4), UintAuthorityId(1), vec![]).is_err()); - assert!(Session::set_keys(Origin::signed(1), UintAuthorityId(10), vec![]).is_ok()); + assert!(Session::set_keys(Origin::signed(4), UintAuthorityId(1).into(), vec![]).is_err()); + assert!(Session::set_keys(Origin::signed(1), UintAuthorityId(10).into(), vec![]).is_ok()); // is fine now that 1 has migrated off. - assert!(Session::set_keys(Origin::signed(4), UintAuthorityId(1), vec![]).is_ok()); + assert!(Session::set_keys(Origin::signed(4), UintAuthorityId(1).into(), vec![]).is_ok()); }); } @@ -760,7 +763,7 @@ mod tests { initialize_block(5); assert!(!session_changed()); - assert_ok!(Session::set_keys(Origin::signed(2), UintAuthorityId(5), vec![])); + assert_ok!(Session::set_keys(Origin::signed(2), UintAuthorityId(5).into(), vec![])); force_new_session(); initialize_block(6); assert!(!session_changed()); @@ -799,4 +802,18 @@ mod tests { assert!(P::should_end_session(13)); } + + #[test] + fn session_keys_generate_output_works_as_set_keys_input() { + with_externalities(&mut new_test_ext(), || { + let new_keys = mock::MockSessionKeys::generate(None); + assert_ok!( + Session::set_keys( + Origin::signed(2), + ::Keys::decode(&mut &new_keys[..]).expect("Decode keys"), + vec![], + ) + ); + }); + } } diff --git a/srml/session/src/mock.rs b/srml/session/src/mock.rs index 734f5bbde4bd6..66bf93032d29f 100644 --- a/srml/session/src/mock.rs +++ b/srml/session/src/mock.rs @@ -19,13 +19,24 @@ use super::*; use std::cell::RefCell; use srml_support::{impl_outer_origin, parameter_types}; -use primitives::H256; +use primitives::{crypto::key_types::DUMMY, H256}; use sr_primitives::{ - Perbill, - traits::{BlakeTwo256, IdentityLookup, ConvertInto}, + Perbill, impl_opaque_keys, traits::{BlakeTwo256, IdentityLookup, ConvertInto}, testing::{Header, UintAuthorityId} }; +impl_opaque_keys! { + pub struct MockSessionKeys { + #[id(DUMMY)] + pub dummy: UintAuthorityId, + } +} + +impl From for MockSessionKeys { + fn from(dummy: UintAuthorityId) -> Self { + Self { dummy } + } +} impl_outer_origin! { pub enum Origin for Test {} @@ -58,7 +69,9 @@ impl SessionHandler for TestSessionHandler { ) { SESSION_CHANGED.with(|l| *l.borrow_mut() = changed); AUTHORITIES.with(|l| - *l.borrow_mut() = validators.iter().map(|(_, id)| id.get::(0).unwrap_or_default()).collect() + *l.borrow_mut() = validators.iter() + .map(|(_, id)| id.get::(DUMMY).unwrap_or_default()) + .collect() ); } fn on_disabled(_validator_index: usize) {} @@ -119,10 +132,12 @@ parameter_types! { pub const MinimumPeriod: u64 = 5; pub const AvailableBlockRatio: Perbill = Perbill::one(); } + impl system::Trait for Test { type Origin = Origin; type Index = u64; type BlockNumber = u64; + type Call = (); type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; @@ -135,13 +150,13 @@ impl system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; } + impl timestamp::Trait for Test { type Moment = u64; type OnTimestampSet = (); type MinimumPeriod = MinimumPeriod; } - impl Trait for Test { type ShouldEndSession = TestShouldEndSession; #[cfg(feature = "historical")] @@ -151,7 +166,7 @@ impl Trait for Test { type SessionHandler = TestSessionHandler; type ValidatorId = u64; type ValidatorIdOf = ConvertInto; - type Keys = UintAuthorityId; + type Keys = MockSessionKeys; type Event = (); type SelectInitialValidators = (); } diff --git a/srml/staking/src/mock.rs b/srml/staking/src/mock.rs index 344ef70e3b6f4..787c4f13b733b 100644 --- a/srml/staking/src/mock.rs +++ b/srml/staking/src/mock.rs @@ -110,6 +110,7 @@ impl system::Trait for Test { type Origin = Origin; type Index = u64; type BlockNumber = BlockNumber; + type Call = (); type Hash = H256; type Hashing = ::sr_primitives::traits::BlakeTwo256; type AccountId = AccountId; diff --git a/srml/support/src/storage/hashed/generator.rs b/srml/support/src/storage/hashed/generator.rs index bc6cd145ceaea..f8b8fb5483e1e 100644 --- a/srml/support/src/storage/hashed/generator.rs +++ b/srml/support/src/storage/hashed/generator.rs @@ -238,6 +238,23 @@ pub trait StorageMap { /// Take the value under a key. fn take>(key: &K, storage: &mut S) -> Self::Query; + /// Swap the values of two keys. + fn swap>(key1: &K, key2: &K, storage: &mut S) { + let k1 = Self::key_for(key1); + let k2 = Self::key_for(key2); + let v1 = storage.get_raw(&k1[..]); + if let Some(val) = storage.get_raw(&k2[..]) { + storage.put_raw(&k1[..], &val[..]); + } else { + storage.kill(&k1[..]) + } + if let Some(val) = v1 { + storage.put_raw(&k2[..], &val[..]); + } else { + storage.kill(&k2[..]) + } + } + /// Store a value to be associated with the given key from the map. fn insert>(key: &K, val: &V, storage: &mut S) { storage.put(&Self::key_for(key)[..], val); diff --git a/srml/support/src/storage/mod.rs b/srml/support/src/storage/mod.rs index 46b6603d91dc0..385fad42eb260 100644 --- a/srml/support/src/storage/mod.rs +++ b/srml/support/src/storage/mod.rs @@ -191,6 +191,9 @@ pub trait StorageMap { /// Load the value associated with the given key from the map. fn get>(key: KeyArg) -> Self::Query; + /// Swap the values of two keys. + fn swap, KeyArg2: Borrow>(key1: KeyArg1, key2: KeyArg2); + /// Store a value to be associated with the given key from the map. fn insert, ValArg: Borrow>(key: KeyArg, val: ValArg); @@ -227,6 +230,10 @@ impl StorageMap for U where U: hashed::generator::S U::get(key.borrow(), &RuntimeStorage) } + fn swap, KeyArg2: Borrow>(key1: KeyArg1, key2: KeyArg2) { + U::swap(key1.borrow(), key2.borrow(), &mut RuntimeStorage) + } + fn insert, ValArg: Borrow>(key: KeyArg, val: ValArg) { U::insert(key.borrow(), val.borrow(), &mut RuntimeStorage) } diff --git a/srml/support/src/traits.rs b/srml/support/src/traits.rs index 5f1d7c32ef40e..2766ba0a98767 100644 --- a/srml/support/src/traits.rs +++ b/srml/support/src/traits.rs @@ -18,7 +18,7 @@ //! //! NOTE: If you're looking for `parameter_types`, it has moved in to the top-level module. -use crate::rstd::{result, marker::PhantomData, ops::Div}; +use crate::rstd::{prelude::*, result, marker::PhantomData, ops::Div}; use crate::codec::{Codec, Encode, Decode}; use primitives::u32_trait::Value as U32; use crate::sr_primitives::traits::{MaybeSerializeDebug, SimpleArithmetic, Saturating}; @@ -631,12 +631,61 @@ impl WithdrawReasons { } /// Trait for type that can handle incremental changes to a set of account IDs. -pub trait ChangeMembers { +pub trait ChangeMembers { + /// A number of members `incoming` just joined the set and replaced some `outgoing` ones. The + /// new set is given by `new`, and need not be sorted. + fn change_members(incoming: &[AccountId], outgoing: &[AccountId], mut new: Vec) { + new.sort_unstable(); + Self::change_members_sorted(incoming, outgoing, &new[..]); + } + /// A number of members `_incoming` just joined the set and replaced some `_outgoing` ones. The - /// new set is thus given by `_new`. - fn change_members(_incoming: &[AccountId], _outgoing: &[AccountId], _new: &[AccountId]); + /// new set is thus given by `sorted_new` and **must be sorted**. + /// + /// NOTE: This is the only function that needs to be implemented in `ChangeMembers`. + fn change_members_sorted( + incoming: &[AccountId], + outgoing: &[AccountId], + sorted_new: &[AccountId], + ); + + /// Set the new members; they **must already be sorted**. This will compute the diff and use it to + /// call `change_members_sorted`. + fn set_members_sorted(new_members: &[AccountId], old_members: &[AccountId]) { + let mut old_iter = old_members.iter(); + let mut new_iter = new_members.iter(); + let mut incoming = Vec::new(); + let mut outgoing = Vec::new(); + let mut old_i = old_iter.next(); + let mut new_i = new_iter.next(); + loop { + match (old_i, new_i) { + (None, None) => break, + (Some(old), Some(new)) if old == new => { + old_i = old_iter.next(); + new_i = new_iter.next(); + } + (Some(old), Some(new)) if old < new => { + outgoing.push(old.clone()); + old_i = old_iter.next(); + } + (Some(old), None) => { + outgoing.push(old.clone()); + old_i = old_iter.next(); + } + (_, Some(new)) => { + incoming.push(new.clone()); + new_i = new_iter.next(); + } + } + } + + Self::change_members_sorted(&incoming[..], &outgoing[..], &new_members); + } } -impl ChangeMembers for () { - fn change_members(_incoming: &[T], _outgoing: &[T], _new_set: &[T]) {} +impl ChangeMembers for () { + fn change_members(_: &[T], _: &[T], _: Vec) {} + fn change_members_sorted(_: &[T], _: &[T], _: &[T]) {} + fn set_members_sorted(_: &[T], _: &[T]) {} } diff --git a/srml/system/benches/bench.rs b/srml/system/benches/bench.rs index 8e13e92d3f8d8..8b67ba50bd655 100644 --- a/srml/system/benches/bench.rs +++ b/srml/system/benches/bench.rs @@ -63,6 +63,7 @@ impl system::Trait for Runtime { type Origin = Origin; type Index = u64; type BlockNumber = u64; + type Call = (); type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index d3a87cd38b354..f41a566724cfc 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -154,6 +154,9 @@ pub trait Trait: 'static + Eq + Clone { /// The aggregated `Origin` type used by dispatchable calls. type Origin: Into, Self::Origin>> + From>; + /// The aggregated `Call` type. + type Call; + /// Account index (aka nonce) type. This stores the number of previous transactions associated with a sender /// account. type Index: @@ -839,7 +842,7 @@ impl CheckWeight { let added_weight = info.weight.min(limit); let next_weight = current_weight.saturating_add(added_weight); if next_weight > limit { - return Err(DispatchError::Resource) + return Err(DispatchError::Exhausted) } Ok(next_weight) } @@ -854,7 +857,7 @@ impl CheckWeight { let added_len = len as u32; let next_len = current_len.saturating_add(added_len); if next_len > limit { - return Err(DispatchError::Resource) + return Err(DispatchError::Exhausted) } Ok(next_len) } @@ -876,6 +879,7 @@ impl CheckWeight { impl SignedExtension for CheckWeight { type AccountId = T::AccountId; + type Call = T::Call; type AdditionalSigned = (); fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) } @@ -883,6 +887,7 @@ impl SignedExtension for CheckWeight { fn pre_dispatch( self, _who: &Self::AccountId, + _call: &Self::Call, info: DispatchInfo, len: usize, ) -> Result<(), DispatchError> { @@ -896,6 +901,7 @@ impl SignedExtension for CheckWeight { fn validate( &self, _who: &Self::AccountId, + _call: &Self::Call, info: DispatchInfo, len: usize, ) -> Result { @@ -936,6 +942,7 @@ impl rstd::fmt::Debug for CheckNonce { impl SignedExtension for CheckNonce { type AccountId = T::AccountId; + type Call = T::Call; type AdditionalSigned = (); fn additional_signed(&self) -> rstd::result::Result<(), &'static str> { Ok(()) } @@ -943,6 +950,7 @@ impl SignedExtension for CheckNonce { fn pre_dispatch( self, who: &Self::AccountId, + _call: &Self::Call, _info: DispatchInfo, _len: usize, ) -> Result<(), DispatchError> { @@ -959,6 +967,7 @@ impl SignedExtension for CheckNonce { fn validate( &self, who: &Self::AccountId, + _call: &Self::Call, info: DispatchInfo, _len: usize, ) -> Result { @@ -1006,6 +1015,7 @@ impl rstd::fmt::Debug for CheckEra { impl SignedExtension for CheckEra { type AccountId = T::AccountId; + type Call = T::Call; type AdditionalSigned = T::Hash; fn additional_signed(&self) -> Result { let current_u64 = >::block_number().saturated_into::(); @@ -1035,6 +1045,7 @@ impl CheckGenesis { impl SignedExtension for CheckGenesis { type AccountId = T::AccountId; + type Call = ::Call; type AdditionalSigned = T::Hash; fn additional_signed(&self) -> Result { Ok(>::block_hash(T::BlockNumber::zero())) @@ -1080,6 +1091,7 @@ mod tests { impl Trait for Test { type Origin = Origin; + type Call = (); type Index = u64; type BlockNumber = u64; type Hash = H256; @@ -1106,6 +1118,8 @@ mod tests { type System = Module; + const CALL: &::Call = &(); + fn new_test_ext() -> runtime_io::TestExternalities { GenesisConfig::default().build_storage::().unwrap().0.into() } @@ -1259,14 +1273,14 @@ mod tests { let info = DispatchInfo::default(); let len = 0_usize; // stale - assert!(CheckNonce::(0).validate(&1, info, len).is_err()); - assert!(CheckNonce::(0).pre_dispatch(&1, info, len).is_err()); + assert!(CheckNonce::(0).validate(&1, CALL, info, len).is_err()); + assert!(CheckNonce::(0).pre_dispatch(&1, CALL, info, len).is_err()); // correct - assert!(CheckNonce::(1).validate(&1, info, len).is_ok()); - assert!(CheckNonce::(1).pre_dispatch(&1, info, len).is_ok()); + assert!(CheckNonce::(1).validate(&1, CALL, info, len).is_ok()); + assert!(CheckNonce::(1).pre_dispatch(&1, CALL, info, len).is_ok()); // future - assert!(CheckNonce::(5).validate(&1, info, len).is_ok()); - assert!(CheckNonce::(5).pre_dispatch(&1, info, len).is_err()); + assert!(CheckNonce::(5).validate(&1, CALL, info, len).is_ok()); + assert!(CheckNonce::(5).pre_dispatch(&1, CALL, info, len).is_err()); }) } @@ -1287,7 +1301,7 @@ mod tests { let reset_check_weight = |i, f, s| { AllExtrinsicsWeight::put(s); - let r = CheckWeight::(PhantomData).pre_dispatch(&1, i, len); + let r = CheckWeight::(PhantomData).pre_dispatch(&1, CALL, i, len); if f { assert!(r.is_err()) } else { assert!(r.is_ok()) } }; @@ -1304,7 +1318,7 @@ mod tests { let len = 0_usize; assert_eq!(System::all_extrinsics_weight(), 0); - let r = CheckWeight::(PhantomData).pre_dispatch(&1, free, len); + let r = CheckWeight::(PhantomData).pre_dispatch(&1, CALL, free, len); assert!(r.is_ok()); assert_eq!(System::all_extrinsics_weight(), 0); }) @@ -1318,7 +1332,7 @@ mod tests { let normal_limit = normal_weight_limit(); assert_eq!(System::all_extrinsics_weight(), 0); - let r = CheckWeight::(PhantomData).pre_dispatch(&1, max, len); + let r = CheckWeight::(PhantomData).pre_dispatch(&1, CALL, max, len); assert!(r.is_ok()); assert_eq!(System::all_extrinsics_weight(), normal_limit); }) @@ -1335,15 +1349,15 @@ mod tests { // given almost full block AllExtrinsicsWeight::put(normal_limit); // will not fit. - assert!(CheckWeight::(PhantomData).pre_dispatch(&1, normal, len).is_err()); + assert!(CheckWeight::(PhantomData).pre_dispatch(&1, CALL, normal, len).is_err()); // will fit. - assert!(CheckWeight::(PhantomData).pre_dispatch(&1, op, len).is_ok()); + assert!(CheckWeight::(PhantomData).pre_dispatch(&1, CALL, op, len).is_ok()); // likewise for length limit. let len = 100_usize; AllExtrinsicsLen::put(normal_length_limit()); - assert!(CheckWeight::(PhantomData).pre_dispatch(&1, normal, len).is_err()); - assert!(CheckWeight::(PhantomData).pre_dispatch(&1, op, len).is_ok()); + assert!(CheckWeight::(PhantomData).pre_dispatch(&1, CALL, normal, len).is_err()); + assert!(CheckWeight::(PhantomData).pre_dispatch(&1, CALL, op, len).is_ok()); }) } @@ -1355,11 +1369,11 @@ mod tests { let len = 0_usize; assert_eq!( - CheckWeight::(PhantomData).validate(&1, normal, len).unwrap().priority, + CheckWeight::(PhantomData).validate(&1, CALL, normal, len).unwrap().priority, 100, ); assert_eq!( - CheckWeight::(PhantomData).validate(&1, op, len).unwrap().priority, + CheckWeight::(PhantomData).validate(&1, CALL, op, len).unwrap().priority, Bounded::max_value(), ); }) @@ -1372,7 +1386,7 @@ mod tests { let normal_limit = normal_weight_limit() as usize; let reset_check_weight = |tx, s, f| { AllExtrinsicsLen::put(0); - let r = CheckWeight::(PhantomData).pre_dispatch(&1, tx, s); + let r = CheckWeight::(PhantomData).pre_dispatch(&1, CALL, tx, s); if f { assert!(r.is_err()) } else { assert!(r.is_ok()) } }; diff --git a/srml/timestamp/src/lib.rs b/srml/timestamp/src/lib.rs index bc9533e6e6dc9..1b522526188e3 100644 --- a/srml/timestamp/src/lib.rs +++ b/srml/timestamp/src/lib.rs @@ -358,6 +358,7 @@ mod tests { type Origin = Origin; type Index = u64; type BlockNumber = u64; + type Call = (); type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; diff --git a/srml/treasury/src/lib.rs b/srml/treasury/src/lib.rs index 1cf3277199431..0183fcd8990ed 100644 --- a/srml/treasury/src/lib.rs +++ b/srml/treasury/src/lib.rs @@ -382,6 +382,7 @@ mod tests { type Origin = Origin; type Index = u64; type BlockNumber = u64; + type Call = (); type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; diff --git a/subkey/src/main.rs b/subkey/src/main.rs index c123e88ec2f7a..5b16bde28d801 100644 --- a/subkey/src/main.rs +++ b/subkey/src/main.rs @@ -88,7 +88,7 @@ impl Crypto for Sr25519 { fn execute(matches: clap::ArgMatches) where <::Pair as Pair>::Signature: AsRef<[u8]> + AsMut<[u8]> + Default, - <::Pair as Pair>::Public: Sized + AsRef<[u8]> + Ss58Codec + AsRef<<::Pair as Pair>::Public>, + <::Pair as Pair>::Public: Sized + AsRef<[u8]> + Ss58Codec, { let extra = |i: Index, f: Balance| { ( diff --git a/test-utils/chain-spec-builder/src/main.rs b/test-utils/chain-spec-builder/src/main.rs index 211e8321e3fd1..13b4cc38a1646 100644 --- a/test-utils/chain-spec-builder/src/main.rs +++ b/test-utils/chain-spec-builder/src/main.rs @@ -1,6 +1,6 @@ use clap::{App, load_yaml}; -use node_cli::chain_spec; +use node_cli::chain_spec::{self, AccountId}; use substrate_service::chain_ops::build_spec; fn genesis_constructor() -> chain_spec::GenesisConfig { @@ -13,11 +13,11 @@ fn genesis_constructor() -> chain_spec::GenesisConfig { let endowed_accounts = matches.values_of("endowed_account_seed") .unwrap() - .map(chain_spec::get_account_id_from_seed) + .map(chain_spec::get_from_seed::) .collect(); let sudo_key_seed = matches.value_of("sudo_key_seed").unwrap(); - let sudo_key = chain_spec::get_account_id_from_seed(sudo_key_seed); + let sudo_key = chain_spec::get_from_seed::(sudo_key_seed); let enable_println = true;