Refactor key management (#3296)

* Add Call type to extensible transactions.

Cleanup some naming

* Merge Resource and BlockExhausted into just Exhausted

* Fix

* Another fix

* Call

* Some fixes

* Fix srml tests.

* Fix all tests.

* Refactor crypto so each application of it has its own type.

* Introduce new AuthorityProvider API into Aura

This will eventually allow for dynamic determination of authority
keys and avoid having to set them directly on CLI.

* Introduce authority determinator for Babe.

Experiment with modular consensus API.

* Work in progress to introduce KeyTypeId and avoid polluting API
with validator IDs

* Finish up drafting imonline

* Rework offchain workers API.

* Rework API implementation.

* Make it compile for wasm, simplify app_crypto.

* Fix compilation of im-online.

* Fix compilation of im-online.

* Fix more compilation errors.

* Make it compile.

* Fixing tests.

* Rewrite `keystore`

* Fix session tests

* Bring back `TryFrom`'s'

* Fix `srml-grandpa`

* Fix `srml-aura`

* Fix consensus babe

* More fixes

* Make service generate keys from dev_seed

* Build fixes

* Remove offchain tests

* More fixes and cleanups

* Fixes finality grandpa

* Fix `consensus-aura`

* Fix cli

* Fix `node-cli`

* Fix chain_spec builder

* Fix doc tests

* Add authority getter for grandpa.

* Test fix

* Fixes

* Make keystore accessible from the runtime

* Move app crypto to its own crate

* Update `Cargo.lock`

* Make the crypto stuff usable from the runtime

* Adds some runtime crypto tests

* Use last finalized block for grandpa authority

* Fix warning

* Adds `SessionKeys` runtime api

* Remove `FinalityPair` and `ConsensusPair`

* Minor governance tweaks to get it inline with docs.

* Make the governance be up to date with the docs.

* Build fixes.

* Generate the inital session keys

* Failing keystore is a hard error

* Make babe work again

* Fix grandpa

* Fix tests

* Disable `keystore` in consensus critical stuff

* Build fix.

* ImOnline supports multiple authorities at once.

* Update core/application-crypto/src/ed25519.rs

* Merge branch 'master' into gav-in-progress

* Remove unneeded code for now.

* Some `session` testing

* Support querying the public keys

* Cleanup offchain

* Remove warnings

* More cleanup

* Apply suggestions from code review

Co-Authored-By: Benjamin Kampmann <ben.kampmann@googlemail.com>

* More cleanups

* JSONRPC API for setting keys.

Also, rename traits::KeyStore* -> traits::BareCryptoStore*

* Bad merge

* Fix integration tests

* Fix test build

* Test fix

* Fixes

* Warnings

* Another warning

* Bump version.
This commit is contained in:
Gavin Wood
2019-08-07 20:47:48 +02:00
committed by GitHub
parent a6a6779f01
commit 1a524b8207
160 changed files with 4467 additions and 2769 deletions
+109 -27
View File
@@ -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)",
]
@@ -4420,9 +4478,9 @@ dependencies = [
"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"
+3
View File
@@ -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",
@@ -0,0 +1,21 @@
[package]
name = "substrate-application-crypto"
version = "2.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
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" ]
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<Self> {
rio::ed25519_public_keys(key_type)
}
fn generate_pair(key_type: KeyTypeId, seed: Option<&str>) -> Self {
rio::ed25519_generate(key_type, seed)
}
fn sign<M: AsRef<[u8]>>(&self, key_type: KeyTypeId, msg: &M) -> Option<Self::Signature> {
rio::ed25519_sign(key_type, self, msg)
}
fn verify<M: AsRef<[u8]>>(&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())));
}
}
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<Item=$crate::DeriveJunction>
>(&self, path: Iter) -> Result<Self, Self::DeriveError> {
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<Self, $crate::SecretStringError> {
<$pair>::from_seed_slice(seed).map(Self)
}
fn from_standard_components<
I: Iterator<Item=$crate::DeriveJunction>
>(
seed: &str,
password: Option<&str>,
path: I,
) -> Result<Self, $crate::SecretStringError> {
<$pair>::from_standard_components::<I>(seed, password, path).map(Self)
}
fn sign(&self, msg: &[u8]) -> Self::Signature {
Signature(self.0.sign(msg))
}
fn verify<M: AsRef<[u8]>>(
sig: &Self::Signature,
message: M,
pubkey: &Self::Public,
) -> bool {
<$pair>::verify(&sig.0, message, pubkey.as_ref())
}
fn verify_weak<P: AsRef<[u8]>, 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<u8> { 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<Iter: Iterator<Item=$crate::DeriveJunction>>(&self,
path: Iter
) -> Option<Self> {
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<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> 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<D>(deserializer: D) -> std::result::Result<Self, D::Error> 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<Signature=$sig> {
type Signature = Signature;
fn all() -> $crate::Vec<Self> {
<$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<M: AsRef<[u8]>>(&self, msg: &M) -> Option<Self::Signature> {
<$public as $crate::RuntimePublic>::sign(
self.as_ref(),
$key_type,
msg,
).map(Signature)
}
fn verify<M: AsRef<[u8]>>(&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
}
}
}
}
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<Self> {
rio::sr25519_public_keys(key_type)
}
fn generate_pair(key_type: KeyTypeId, seed: Option<&str>) -> Self {
rio::sr25519_generate(key_type, seed)
}
fn sign<M: AsRef<[u8]>>(&self, key_type: KeyTypeId, msg: &M) -> Option<Self::Signature> {
rio::sr25519_sign(key_type, self, msg)
}
fn verify<M: AsRef<[u8]>>(&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())));
}
}
@@ -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 <http://www.gnu.org/licenses/>.
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<Self>;
/// 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<T: std::fmt::Debug + std::hash::Hash> 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<T> 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<Self> + Public + Ord + PartialOrd + Eq + PartialEq + MaybeDebugHash + codec::Codec;
}
/// A application's key pair.
#[cfg(feature = "std")]
pub trait AppPair: AppKey + Pair<Public=<Self as AppKey>::Public> {
/// The wrapped type which is just a plain instance of `Pair`.
type Generic: IsWrappedBy<Self> + Pair<Public=<<Self as AppKey>::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<Self> + 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<Self>;
/// 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<M: AsRef<[u8]>>(&self, key_type: KeyTypeId, msg: &M) -> Option<Self::Signature>;
/// Verify that the given signature matches the given message using this public key.
fn verify<M: AsRef<[u8]>>(&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<Self>;
/// 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<M: AsRef<[u8]>>(&self, msg: &M) -> Option<Self::Signature>;
/// Verify that the given signature matches the given message using this public key.
fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool;
}
+28 -13
View File
@@ -366,6 +366,24 @@ fn input_keystore_password() -> Result<String, String> {
.map_err(|e| format!("{:?}", e))
}
/// Fill the password field of the given config instance.
fn fill_config_keystore_password<C, G>(
config: &mut service::Configuration<C, G>,
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<F, S>(
cli: RunCmd, spec_factory: S, impl_name: &'static str, version: &VersionInfo
) -> error::Result<FactoryFullConfiguration<F>>
@@ -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 {
config.dev_key_seed = cli.keyring.account
.map(|a| format!("//{}", a))
.or_else(|| Some("//Alice".into()));
}
if cli.shared_params.dev && cli.keyring.account.is_none() {
config.keys.push("//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" };
+34 -13
View File
@@ -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<client::ExecutionStrategy> 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<PathBuf>,
/// Specify additional key seed
#[structopt(long = "key", value_name = "STRING")]
pub key: Option<String>,
/// 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<PathBuf>,
/// 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<String>,
/// 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<PathBuf>
}
/// 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::<Vec<_>>();
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)
)
})
+1 -1
View File
@@ -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 }
+1 -1
View File
@@ -5,7 +5,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
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
+2 -1
View File
@@ -195,6 +195,7 @@ pub fn new_client<E, S, Block, RA>(
executor: E,
genesis_storage: S,
execution_strategies: ExecutionStrategies,
keystore: Option<primitives::traits::BareCryptoStorePtr>,
) -> Result<
client::Client<Backend<Block>,
client::LocalCallExecutor<Backend<Block>, E>, Block, RA>, client::error::Error
@@ -205,7 +206,7 @@ pub fn new_client<E, S, Block, RA>(
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)?)
}
+29 -2
View File
@@ -83,6 +83,7 @@ where
native_call: Option<NC>,
side_effects_handler: Option<&mut O>,
proof_recorder: &Option<Rc<RefCell<ProofRecorder<B>>>>,
enable_keystore: bool,
) -> error::Result<NativeOrEncoded<R>> where ExecutionManager<EM>: Clone;
/// Extract RuntimeVersion of given block
@@ -150,14 +151,20 @@ where
pub struct LocalCallExecutor<B, E> {
backend: Arc<B>,
executor: E,
keystore: Option<primitives::traits::BareCryptoStorePtr>,
}
impl<B, E> LocalCallExecutor<B, E> {
/// Creates new instance of local call executor.
pub fn new(backend: Arc<B>, executor: E) -> Self {
pub fn new(
backend: Arc<B>,
executor: E,
keystore: Option<primitives::traits::BareCryptoStorePtr>,
) -> Self {
LocalCallExecutor {
backend,
executor,
keystore,
}
}
}
@@ -167,6 +174,7 @@ impl<B, E> Clone for LocalCallExecutor<B, E> 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<NC>,
side_effects_handler: Option<&mut O>,
recorder: &Option<Rc<RefCell<ProofRecorder<Block>>>>,
enable_keystore: bool,
) -> Result<NativeOrEncoded<R>, error::Error> where ExecutionManager<EM>: 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<Block>) -> error::Result<RuntimeVersion> {
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)
}
+63 -50
View File
@@ -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<Block> = mpsc::UnboundedReceiver<BlockImportNotification<Block>>;
@@ -260,6 +253,7 @@ impl<H> PrePostHeader<H> {
pub fn new_in_mem<E, Block, S, RA>(
executor: E,
genesis_storage: S,
keystore: Option<primitives::traits::BareCryptoStorePtr>,
) -> error::Result<Client<
in_mem::Backend<Block, Blake2Hasher>,
LocalCallExecutor<in_mem::Backend<Block, Blake2Hasher>, E>,
@@ -270,7 +264,7 @@ pub fn new_in_mem<E, Block, S, RA>(
S: BuildStorage,
Block: BlockT<Hash=H256>,
{
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<B, E, Block, S, RA>(
backend: Arc<B>,
executor: E,
build_genesis_storage: S,
keystore: Option<primitives::traits::BareCryptoStorePtr>,
) -> error::Result<Client<B, LocalCallExecutor<B, E>, Block, RA>>
where
E: CodeExecutor<Blake2Hasher> + RuntimeInfo,
@@ -286,10 +281,24 @@ pub fn new_with_backend<B, E, Block, S, RA>(
Block: BlockT<Hash=H256>,
B: backend::LocalBackend<Block, Blake2Hasher>
{
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<Hash=H256>;
}
impl<B, E, Block, RA> BlockOf for Client<B, E, Block, RA> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
Block: BlockT<Hash=H256>,
{
type Type = Block;
}
impl<B, E, Block, RA> Client<B, E, Block, RA> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
@@ -362,7 +371,8 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
pub fn storage(&self, id: &BlockId<Block>, key: &StorageKey) -> error::Result<Option<StorageData>> {
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<B, E, Block, RA> CallRuntimeAt<Block> for Client<B, E, Block, RA> where
context: ExecutionContext,
recorder: &Option<Rc<RefCell<ProofRecorder<Block>>>>,
) -> error::Result<NativeOrEncoded<R>> {
let enable_keystore = context.enable_keystore();
let manager = match context {
ExecutionContext::BlockConstruction =>
self.execution_strategies.block_construction.get_manager(),
@@ -1443,6 +1455,7 @@ impl<B, E, Block, RA> CallRuntimeAt<Block> for Client<B, E, Block, RA> where
native_call,
offchain_extensions.as_mut(),
recorder,
enable_keystore,
)
}
+9 -3
View File
@@ -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,
);
+3 -2
View File
@@ -62,7 +62,8 @@
//! backend.clone(),
//! LocalCallExecutor::new(
//! backend.clone(),
//! NativeExecutor::<LocalExecutor>::new(None)
//! NativeExecutor::<LocalExecutor>::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")]
@@ -94,8 +94,8 @@ where
call_data: &[u8],
_strategy: ExecutionStrategy,
_side_effects_handler: Option<&mut O>,
)
-> ClientResult<Vec<u8>> {
) -> ClientResult<Vec<u8>>
{
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<NC>,
side_effects_handler: Option<&mut O>,
_recorder: &Option<Rc<RefCell<ProofRecorder<Block>>>>,
_enable_keystore: bool,
) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: 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<Block>) -> ClientResult<RuntimeVersion> {
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<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> for
native_call: Option<NC>,
side_effects_handler: Option<&mut O>,
recorder: &Option<Rc<RefCell<ProofRecorder<Block>>>>,
enable_keystore: bool,
) -> ClientResult<NativeOrEncoded<R>> where ExecutionManager<EM>: 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<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> 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<Block, B, Remote, Local> CallExecutor<Block, Blake2Hasher> 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<Header, E, H>(
executor,
"Core_initialize_block",
&next_block.encode(),
None,
)?;
// execute method
@@ -472,6 +489,7 @@ pub fn check_execution_proof<Header, E, H>(
executor,
&request.method,
&request.call_data,
None,
)?;
Ok(local_result)
@@ -424,7 +424,6 @@ impl<E, Block, H, S, F> FetchChecker<Block> for LightDataChecker<E, H, Block, S,
request: &RemoteBodyRequest<Block::Header>,
body: Vec<Block::Extrinsic>
) -> ClientResult<Vec<Block::Extrinsic>> {
// TODO: #2621
let extrinsics_root = HashFor::<Block>::ordered_trie_root(body.iter().map(Encode::encode));
if *request.header.extrinsics_root() == extrinsics_root {
+1 -1
View File
@@ -73,7 +73,7 @@ pub fn new_light<B, S, F, GS, RA, E>(
E: CodeExecutor<Blake2Hasher> + 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())
}
+3 -1
View File
@@ -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]
@@ -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",
]
@@ -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'];
+18 -17
View File
@@ -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<B, C, SC, E, I, P, SO, Error, H>(
force_authoring: bool,
) -> Result<impl futures01::Future<Item = (), Error = ()>, consensus_common::Error> where
B: BlockT<Header=H>,
C: ProvideRuntimeApi + ProvideCache<B> + AuxStore + Send + Sync,
C: ProvideRuntimeApi + BlockOf + ProvideCache<B> + AuxStore + Send + Sync,
C::Api: AuraApi<B, AuthorityId<P>>,
SC: SelectChain<B>,
E::Proposer: Proposer<B, Error=Error>,
@@ -188,7 +189,7 @@ struct AuraWorker<C, E, I, P, SO> {
impl<H, B, C, E, I, P, Error, SO> SlotWorker<B> for AuraWorker<C, E, I, P, SO> where
B: BlockT<Header=H>,
C: ProvideRuntimeApi + ProvideCache<B> + Sync,
C: ProvideRuntimeApi + BlockOf + ProvideCache<B> + Sync,
C::Api: AuraApi<B, AuthorityId<P>>,
E: Environment<B, Error=Error>,
E::Proposer: Proposer<B, Error=Error>,
@@ -397,7 +398,7 @@ fn check_header<C, B: BlockT, P: Pair>(
DigestItemFor<B>: CompatibleDigestItem<P>,
P::Signature: Decode,
C: client::backend::AuxStore,
P::Public: AsRef<P::Public> + 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<C, P> AuraVerifier<C, P>
#[forbid(deprecated)]
impl<B: BlockT, C, P> Verifier<B> for AuraVerifier<C, P> where
C: ProvideRuntimeApi + Send + Sync + client::backend::AuxStore + ProvideCache<B>,
C: ProvideRuntimeApi + Send + Sync + client::backend::AuxStore + ProvideCache<B> + BlockOf,
C::Api: BlockBuilderApi<B> + AuraApi<B, AuthorityId<P>>,
DigestItemFor<B>: CompatibleDigestItem<P>,
P: Pair + Send + Sync + 'static,
P::Public: Send + Sync + Hash + Eq + Clone + Decode + Encode + Debug + AsRef<P::Public> + 'static,
P::Public: Send + Sync + Hash + Eq + Clone + Decode + Encode + Debug + 'static,
P::Signature: Encode + Decode,
{
fn verify(
@@ -610,7 +611,7 @@ impl<B: BlockT, C, P> Verifier<B> for AuraVerifier<C, P> where
fn initialize_authorities_cache<A, B, C>(client: &C) -> Result<(), ConsensusError> where
A: Codec,
B: BlockT,
C: ProvideRuntimeApi + ProvideCache<B>,
C: ProvideRuntimeApi + BlockOf + ProvideCache<B>,
C::Api: AuraApi<B, A>,
{
// no cache => no initialization
@@ -644,7 +645,7 @@ fn initialize_authorities_cache<A, B, C>(client: &C) -> Result<(), ConsensusErro
fn authorities<A, B, C>(client: &C, at: &BlockId<B>) -> Result<Vec<A>, ConsensusError> where
A: Codec,
B: BlockT,
C: ProvideRuntimeApi + ProvideCache<B>,
C: ProvideRuntimeApi + BlockOf + ProvideCache<B>,
C::Api: AuraApi<B, A>,
{
client
@@ -685,11 +686,11 @@ pub fn import_queue<B, C, P>(
inherent_data_providers: InherentDataProviders,
) -> Result<AuraImportQueue<B>, consensus_common::Error> where
B: BlockT,
C: 'static + ProvideRuntimeApi + ProvideCache<B> + Send + Sync + AuxStore,
C: 'static + ProvideRuntimeApi + BlockOf + ProvideCache<B> + Send + Sync + AuxStore,
C::Api: BlockBuilderApi<B> + AuraApi<B, AuthorityId<P>>,
DigestItemFor<B>: CompatibleDigestItem<P>,
P: Pair + Send + Sync + 'static,
P::Public: Clone + Eq + Send + Sync + Hash + Debug + Encode + Decode + AsRef<P::Public>,
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<PeersFullClient, sr25519::Pair>;
type Verifier = AuraVerifier<PeersFullClient, AuthorityPair>;
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()
]);
}
}
+8 -5
View File
@@ -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 = []
@@ -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",
]
@@ -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<BabePreDigest>;
/// 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<Signature>;
fn as_babe_seal(&self) -> Option<AuthoritySignature>;
/// If this item is a BABE epoch, return it.
fn as_babe_epoch(&self) -> Option<Epoch>;
@@ -132,11 +132,11 @@ impl<Hash> CompatibleDigestItem for DigestItem<Hash> 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<Signature> {
fn as_babe_seal(&self) -> Option<AuthoritySignature> {
self.try_to(OpaqueDigestItemId::Seal(&BABE_ENGINE_ID))
}
@@ -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";
+34 -33
View File
@@ -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<C, E, I, SO, SC> {
/// 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<sr25519::Pair>,
/// The keystore that manages the keys of the node.
pub keystore: KeyStorePtr,
/// The client to use
pub client: Arc<C>,
@@ -171,8 +168,8 @@ pub struct BabeParams<C, E, I, SO, SC> {
/// Start the babe worker. The returned future should be run in a tokio runtime.
pub fn start_babe<B, C, SC, E, I, SO, Error, H>(BabeParams {
config,
local_key,
client,
keystore,
select_chain,
block_import,
env,
@@ -200,10 +197,10 @@ pub fn start_babe<B, C, SC, E, I, SO, Error, H>(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<C, E, I, SO> {
client: Arc<C>,
block_import: Arc<Mutex<I>>,
env: E,
local_key: Arc<sr25519::Pair>,
sync_oracle: SO,
force_authoring: bool,
c: (u64, u64),
keystore: KeyStorePtr,
}
impl<Hash, H, B, C, E, I, Error, SO> SlotWorker<B> for BabeWorker<C, E, I, SO> where
@@ -248,7 +245,6 @@ impl<Hash, H, B, C, E, I, Error, SO> SlotWorker<B> for BabeWorker<C, E, I, SO> 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<Hash, H, B, C, E, I, Error, SO> SlotWorker<B> for BabeWorker<C, E, I, SO> 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<Hash, H, B, C, E, I, Error, SO> SlotWorker<B> for BabeWorker<C, E, I, SO> 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<Hash, H, B, C, E, I, Error, SO> SlotWorker<B> for BabeWorker<C, E, I, SO> 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<Hash, H, B, C, E, I, Error, SO> SlotWorker<B> for BabeWorker<C, E, I, SO> 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::<B>::babe_seal(signature);
let import_block = BlockImportParams::<B> {
@@ -496,7 +492,7 @@ fn check_header<B: BlockT + Sized, C: AuxStore>(
} 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::<AuthorityPair>(&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<B, C>(client: &C) -> Result<(), ConsensusError> where
@@ -1201,8 +1202,8 @@ pub mod test_helpers {
client: &C,
at: &BlockId<B>,
slot_number: u64,
key: &sr25519::Pair,
c: (u64, u64),
keystore: &KeyStorePtr,
) -> Option<BabePreDigest> where
B: BlockT,
C: ProvideRuntimeApi + ProvideCache<B>,
@@ -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(),
+29 -20
View File
@@ -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::<AuthorityPair>(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::<AuthorityPair>("//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),
]);
}
+1 -1
View File
@@ -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" }
+1 -1
View File
@@ -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"
@@ -26,7 +26,7 @@ use runtime_io;
fn check_message_sig<B: Codec, H: Codec>(
message: Message<B, H>,
signature: &Signature,
from: &AuthorityId
from: &AuthorityId,
) -> bool {
let msg: Vec<u8> = message.encode();
runtime_io::ed25519_verify(&signature.0, &msg, from)
+1 -1
View File
@@ -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]
+1 -1
View File
@@ -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"
@@ -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::<primitives::Blake2Hasher>(
+165 -156
View File
@@ -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())
+20 -5
View File
@@ -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<Option<$crate::wasmi::RuntimeValue>, $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 )* } ),*
};
}
}
);
+4 -1
View File
@@ -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"]
@@ -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",
]
@@ -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";
@@ -431,6 +431,7 @@ impl<H, N: Add<Output=N> + Clone> PendingChange<H, N> {
#[cfg(test)]
mod tests {
use super::*;
use primitives::crypto::Public;
fn static_is_descendent_of<A>(value: bool)
-> impl Fn(&A, &A) -> Result<bool, std::io::Error>
@@ -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(),
@@ -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()], |_, _| {});
@@ -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<B: BlockT, N: Network<B>> NetworkBridge<B, N> {
round: Round,
set_id: SetId,
voters: Arc<VoterSet<AuthorityId>>,
local_key: Option<Arc<ed25519::Pair>>,
local_key: Option<AuthorityPair>,
has_voted: HasVoted<B>,
) -> (
impl Stream<Item=SignedMessage<B>,Error=Error>,
@@ -354,8 +354,7 @@ impl<B: BlockT, N: Network<B>> NetworkBridge<B, N> {
);
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<Block: BlockT>(
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<Block: BlockT>(
struct OutgoingMessages<Block: BlockT, N: Network<Block>> {
round: u64,
set_id: u64,
locals: Option<(Arc<ed25519::Pair>, AuthorityId)>,
locals: Option<(AuthorityPair, AuthorityId)>,
sender: mpsc::UnboundedSender<SignedMessage<Block>>,
network: N,
has_voted: HasVoted<Block>,
@@ -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()))
}
@@ -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<dyn Future<Item = (), Error = Self::Error> + Send>;
type Id = AuthorityId;
type Signature = ed25519::Signature;
type Signature = AuthoritySignature;
// regular round message streams
type In = Box<dyn Stream<
@@ -500,15 +500,25 @@ where
let prevote_timer = Delay::new(now + self.config.gossip_duration * 2);
let precommit_timer = Delay::new(now + self.config.gossip_duration * 4);
let local_key = self.config.local_key.as_ref()
.filter(|pair| self.voters.contains_key(&pair.public().into()));
let local_key = crate::is_voter(&self.voters, &self.config.keystore);
let has_voted = match self.voter_set_state.has_voted() {
HasVoted::Yes(id, vote) => {
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<Block>) -> 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<Block>) -> 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<Block>) -> 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(()),
};
@@ -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<Header>;
@@ -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::<TestJustification>::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,
+31 -44
View File
@@ -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::traits::{
NumberFor, Block as BlockT, DigestFor, ProvideRuntimeApi,
};
use fg_primitives::GrandpaApi;
use inherents::InherentDataProviders;
use sr_primitives::generic::BlockId;
use sr_primitives::traits::{
NumberFor, Block as BlockT, DigestFor, ProvideRuntimeApi
};
use fg_primitives::{GrandpaApi, AuthorityPair};
use keystore::KeyStorePtr;
use inherents::InherentDataProviders;
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<Arc<ed25519::Pair>>,
/// Some local identifier of the voter.
pub name: Option<String>,
/// The keystore that manages the keys of this node.
pub keystore: Option<keystore::KeyStorePtr>,
}
impl Config {
@@ -396,11 +399,11 @@ where
}
fn global_communication<Block: BlockT<Hash=H256>, B, E, N, RA>(
local_key: Option<&Arc<ed25519::Pair>>,
set_id: u64,
voters: &Arc<VoterSet<AuthorityId>>,
client: &Arc<Client<B, E, Block, RA>>,
network: &NetworkBridge<Block, N>,
keystore: &Option<KeyStorePtr>,
) -> (
impl Stream<
Item = CommunicationInH<Block, H256>,
@@ -417,10 +420,7 @@ fn global_communication<Block: BlockT<Hash=H256>, B, E, N, RA>(
RA: Send + Sync,
NumberFor<Block>: 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<B, E, Block: BlockT<Hash=H256>, N, RA, SC, X> {
/// block import worker that has already been instantiated with `block_import`.
pub fn run_grandpa_voter<B, E, Block: BlockT<Hash=H256>, N, RA, SC, X>(
grandpa_params: GrandpaParams<B, E, Block, N, RA, SC, X>,
) -> ::client::error::Result<impl Future<Item=(),Error=()> + Send + 'static> where
) -> client::error::Result<impl Future<Item=(),Error=()> + Send + 'static> where
Block::Hash: Ord,
B: Backend<Block, Blake2Hasher> + 'static,
E: CallExecutor<Block, Blake2Hasher> + Send + Sync + 'static,
@@ -568,31 +568,6 @@ pub fn run_grandpa_voter<B, E, Block: BlockT<Hash=H256>, 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<B, E, Block: BlockT<Hash=H256>, 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<B, E, Block: BlockT<Hash=H256>, 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<VoterSet<AuthorityId>>,
keystore: &Option<KeyStorePtr>,
) -> Option<AuthorityPair> {
match keystore {
Some(keystore) => voters.voters().iter()
.find_map(|(p, _)| keystore.read().key_pair::<AuthorityPair>(&p).ok()),
None => None,
}
}
@@ -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::<Block>::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::<Block>::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::<H256, u64>::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())]);
}
}
@@ -185,11 +185,11 @@ pub fn run_grandpa_observer<B, E, Block: BlockT<Hash=H256>, 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;
+66 -28
View File
@@ -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<Block> for RuntimeApi {
_: ExecutionContext,
_: Option<()>,
_: Vec<u8>,
) -> Result<NativeOrEncoded<Vec<(primitives::ed25519::Public, u64)>>> {
) -> Result<NativeOrEncoded<Vec<(AuthorityId, u64)>>> {
Ok(self.inner.genesis_authorities.clone()).map(NativeOrEncoded::Native)
}
@@ -342,11 +342,17 @@ impl AuthoritySetForFinalityChecker<Block> 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::<AuthorityPair>(&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<F>(
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<F>(
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::<HashSet<_>>() // 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<Mutex<GrandpaTestNet>>| -> Box<dyn Future<Item=(), Error=()> + Send> {
let voter = |keystore, peer_id, link, net: Arc<Mutex<GrandpaTestNet>>| -> Box<dyn Future<Item=(), Error=()> + 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);
}
+1 -1
View File
@@ -5,7 +5,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
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 }
+30 -15
View File
@@ -36,18 +36,7 @@ pub enum Keyring {
impl Keyring {
pub fn from_public(who: &Public) -> Option<Keyring> {
[
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<Keyring> {
@@ -83,6 +72,14 @@ impl Keyring {
pub fn iter() -> impl Iterator<Item=Keyring> {
<Self as strum::IntoEnumIterator>::iter()
}
pub fn public(self) -> Public {
self.pair().public()
}
pub fn to_seed(self) -> String {
format!("//{}", self)
}
}
impl From<Keyring> 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(),
)
);
}
}
+31 -25
View File
@@ -37,18 +37,7 @@ pub enum Keyring {
impl Keyring {
pub fn from_public(who: &Public) -> Option<Keyring> {
[
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<Keyring> {
@@ -84,6 +73,14 @@ impl Keyring {
pub fn iter() -> impl Iterator<Item=Keyring> {
<Self as strum::IntoEnumIterator>::iter()
}
pub fn public(self) -> Public {
self.pair().public()
}
pub fn to_seed(self) -> String {
format!("//{}", self)
}
}
impl From<Keyring> for &'static str {
@@ -109,16 +106,7 @@ impl From<Keyring> for sr_primitives::MultiSigner {
lazy_static! {
static ref PRIVATE_KEYS: HashMap<Keyring, Pair> = {
[
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<Keyring, Public> = {
@@ -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(),
)
);
}
}
+3 -1
View File
@@ -5,12 +5,14 @@ authors = ["Parity Technologies <admin@parity.io>"]
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"
+267 -62
View File
@@ -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<RwLock<Store>>;
/// 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<u8>), Vec<u8>>,
password: Option<Protected<String>>,
}
impl Store {
/// Create a new store at the given path.
pub fn open(path: PathBuf) -> Result<Self> {
/// Open the store at the given path.
///
/// Optionally takes a password that will be used to encrypt/decrypt the keys.
pub fn open<T: Into<PathBuf>>(path: T, password: Option<Protected<String>>) -> Result<KeyStorePtr> {
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<TPair: Pair>(&self, public: &TPair::Public) -> Result<Option<TPair>> {
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<Pair: PairT>(
&self,
public: &Pair::Public,
key_type: KeyTypeId,
) -> Result<Option<Pair>> {
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<TPair: 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<Pair: PairT>(&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<TPair: Pair>(&self, password: &str) -> Result<TPair> {
let (pair, phrase, _) = TPair::generate_with_phrase(Some(password));
let mut file = File::create(self.key_file_path::<TPair>(&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<Pair: PairT>(&self, key_type: KeyTypeId, suri: &str) -> Result<Pair> {
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<Pair: AppPair>(&self, suri: &str) -> Result<Pair> {
self.insert_by_type::<Pair::Generic>(Pair::ID, suri).map(Into::into)
}
/// Generate a new key.
///
/// Places it into the file system store.
pub fn generate_by_type<Pair: PairT>(&self, key_type: KeyTypeId) -> Result<Pair> {
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<TPair: Pair>(&mut self, seed: &str) -> Result<TPair> {
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<Pair: AppPair>(&self) -> Result<Pair> {
self.generate_by_type::<Pair::Generic>(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<Pair: PairT>(
&mut self,
seed: &str,
key_type: KeyTypeId,
) -> Result<Pair> {
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<TPair: Pair>(&self, public: &TPair::Public, password: &str) -> Result<TPair> {
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<Pair: AppPair>(&mut self, seed: &str) -> Result<Pair> {
self.insert_ephemeral_from_seed_by_type::<Pair::Generic>(seed, Pair::ID).map(Into::into)
}
/// Get a key pair for the given public key and key type.
pub fn key_pair_by_type<Pair: PairT>(&self,
public: &Pair::Public,
key_type: KeyTypeId,
) -> Result<Pair> {
if let Some(pair) = self.get_additional_pair(public, key_type)? {
return Ok(pair)
}
let path = self.key_file_path::<TPair>(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<TPublic: Public>(&self) -> Result<Vec<TPublic>> {
/// Get a key pair for the given public key.
pub fn key_pair<Pair: AppPair>(&self, public: &<Pair as AppKey>::Public) -> Result<Pair> {
self.key_pair_by_type::<Pair::Generic>(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<TPublic: Public>(&self, key_type: KeyTypeId) -> Result<Vec<TPublic>> {
let mut public_keys: Vec<TPublic> = 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<TPair: Pair>(&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<Public: AppPublic>(&self) -> Result<Vec<Public>> {
self.public_keys_by_type::<Public::Generic>(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<sr25519::Public> {
self.public_keys_by_type::<sr25519::Public>(key_type).unwrap_or_default()
}
fn sr25519_generate_new(
&mut self,
id: KeyTypeId,
seed: Option<&str>,
) -> std::result::Result<sr25519::Public, String> {
let pair = match seed {
Some(seed) => self.insert_ephemeral_from_seed_by_type::<sr25519::Pair>(seed, id),
None => self.generate_by_type::<sr25519::Pair>(id),
}.map_err(|e| e.to_string())?;
Ok(pair.public())
}
fn sr25519_key_pair(&self, id: KeyTypeId, pub_key: &sr25519::Public) -> Option<sr25519::Pair> {
self.key_pair_by_type::<sr25519::Pair>(pub_key, id).ok()
}
fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec<ed25519::Public> {
self.public_keys_by_type::<ed25519::Public>(key_type).unwrap_or_default()
}
fn ed25519_generate_new(
&mut self,
id: KeyTypeId,
seed: Option<&str>,
) -> std::result::Result<ed25519::Public, String> {
let pair = match seed {
Some(seed) => self.insert_ephemeral_from_seed_by_type::<ed25519::Pair>(seed, id),
None => self.generate_by_type::<ed25519::Pair>(id),
}.map_err(|e| e.to_string())?;
Ok(pair.public())
}
fn ed25519_key_pair(&self, id: KeyTypeId, pub_key: &ed25519::Public) -> Option<ed25519::Pair> {
self.key_pair_by_type::<ed25519::Pair>(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::<ed25519::Public>().unwrap().is_empty());
assert!(store.read().public_keys::<ed25519::AppPublic>().unwrap().is_empty());
let key: ed25519::Pair = store.generate("thepassword").unwrap();
let key2: ed25519::Pair = store.load(&key.public(), "thepassword").unwrap();
assert!(store.load::<ed25519::Pair>(&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::<ed25519::Public>().unwrap()[0], key.public());
assert_eq!(store.read().public_keys::<ed25519::AppPublic>().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::<ed25519::AppPair>(&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::<ed25519::AppPair>(&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::<ed25519::AppPair>(&pair.public()).is_err());
let store = Store::open(temp_dir.path(), Some(password.into())).unwrap();
assert_eq!(
pair.public(),
store.read().key_pair::<ed25519::AppPair>(&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::<ed25519::AppPair>().unwrap().public());
public_keys.push(store.write().insert_ephemeral_from_seed::<ed25519::AppPair>(
&format!("0x3d97c819d68f9bafa7d6e79cb991eebcd7{}d966c5334c0b94d9e1fa7ad0869dc", i),
).unwrap().public());
}
// Generate a key of a different type
store.write().generate::<sr25519::AppPair>().unwrap();
public_keys.sort();
let mut store_pubs = store.read().public_keys::<ed25519::AppPublic>().unwrap();
store_pubs.sort();
assert_eq!(public_keys, store_pubs);
}
}
+2 -1
View File
@@ -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" }
+5
View File
@@ -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()
+1 -1
View File
@@ -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
+2 -1
View File
@@ -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"
+18 -295
View File
@@ -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<u8>),
}
/// 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<LocalKey, ()> {
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<Vec<u8>, ()> {
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<Vec<u8>, ()> {
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<bool, ()> {
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<ConsensusPair, FinalityPair> {
LocalKey(LocalKey),
AuthorityKey(ConsensusPair),
FgAuthorityKey(FinalityPair),
}
impl<ConsensusPair: Pair, FinalityPair: Pair> Key<ConsensusPair, FinalityPair> {
fn public(&self) -> Result<Vec<u8>, ()> {
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<Vec<u8>, ()> {
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<bool, ()> {
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<Storage, KeyProvider, Block: traits::Block> {
pub(crate) struct Api<Storage, Block: traits::Block> {
sender: mpsc::UnboundedSender<ExtMessage>,
db: Storage,
keys_password: Protected<String>,
key_provider: KeyProvider,
network_state: Arc<dyn NetworkStateInfo + Send + Sync>,
at: BlockId<Block>,
_at: BlockId<Block>,
}
fn unavailable_yet<R: Default>(name: &str) -> R {
@@ -199,59 +57,10 @@ fn unavailable_yet<R: Default>(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<Storage, KeyProvider, Block> Api<Storage, KeyProvider, Block> where
Storage: OffchainStorage,
KeyProvider: AuthorityKeyProvider<Block>,
Block: traits::Block,
{
fn password(&self) -> Option<&str> {
Some(self.keys_password.as_ref().as_str())
}
fn read_key(
&self,
key: CryptoKey,
) -> Result<Key<KeyProvider::ConsensusPair, KeyProvider::FinalityPair>, ()> {
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<Storage, KeyProvider, Block> OffchainExt for Api<Storage, KeyProvider, Block>
impl<Storage, Block> OffchainExt for Api<Storage, Block>
where
Storage: OffchainStorage,
KeyProvider: AuthorityKeyProvider<Block>,
Block: traits::Block,
{
fn submit_transaction(&mut self, ext: Vec<u8>) -> Result<(), ()> {
@@ -261,30 +70,6 @@ where
.map_err(|_| ())
}
fn new_crypto_key(&mut self, kind: CryptoKind) -> Result<CryptoKey, ()> {
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<Vec<u8>, ()> {
self.read_key(key)?.public()
}
fn network_state(&self) -> Result<OpaqueNetworkState, ()> {
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<Vec<u8>, ()> {
unavailable_yet::<()>("encrypt");
Err(())
}
fn decrypt(&mut self, _key: CryptoKey, _data: &[u8]) -> Result<Vec<u8>, ()> {
unavailable_yet::<()>("decrypt");
Err(())
}
fn sign(&mut self, key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()> {
self.read_key(key)?.sign(data)
}
fn verify(&mut self, key: CryptoKey, msg: &[u8], signature: &[u8]) -> Result<bool, ()> {
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<A: ChainApi> {
impl<A: ChainApi> AsyncApi<A> {
/// Creates new Offchain extensions API implementation an the asynchronous processing part.
pub fn new<S: OffchainStorage, P: AuthorityKeyProvider<A::Block>>(
pub fn new<S: OffchainStorage>(
transaction_pool: Arc<Pool<A>>,
db: S,
keys_password: Protected<String>,
key_provider: P,
at: BlockId<A::Block>,
network_state: Arc<dyn NetworkStateInfo + Send + Sync>,
) -> (Api<S, P, A::Block>, AsyncApi<A>) {
) -> (Api<S, A::Block>, AsyncApi<A>) {
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<LocalStorage, TestProvider<Block>, Block>, AsyncApi<impl ChainApi>) {
fn offchain_api() -> (Api<LocalStorage, Block>, AsyncApi<impl ChainApi>) {
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
+14 -89
View File
@@ -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<Block: traits::Block>: 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<Block>) -> Option<Self::ConsensusPair>;
/// Returns currently configured finality gadget authority key.
fn fg_authority_key(&self, block_id: &BlockId<Block>) -> Option<Self::FinalityPair>;
}
/// An offchain workers manager.
pub struct OffchainWorkers<
Client,
Storage,
KeyProvider,
Block: traits::Block,
> {
pub struct OffchainWorkers<Client, Storage, Block: traits::Block> {
client: Arc<Client>,
db: Storage,
authority_key: KeyProvider,
keys_password: crypto::Protected<String>,
_block: PhantomData<Block>,
}
impl<Client, Storage, KeyProvider, Block: traits::Block> OffchainWorkers<
Client,
Storage,
KeyProvider,
Block,
> {
impl<Client, Storage, Block: traits::Block> OffchainWorkers<Client, Storage, Block> {
/// Creates new `OffchainWorkers`.
pub fn new(
client: Arc<Client>,
db: Storage,
authority_key: KeyProvider,
keys_password: crypto::Protected<String>,
) -> Self {
pub fn new(client: Arc<Client>, db: Storage) -> Self {
Self {
client,
db,
authority_key,
keys_password,
_block: PhantomData,
}
}
}
impl<Client, Storage, KeyProvider, Block: traits::Block> fmt::Debug for OffchainWorkers<
impl<Client, Storage, Block: traits::Block> fmt::Debug for OffchainWorkers<
Client,
Storage,
KeyProvider,
Block,
> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -121,16 +81,14 @@ impl<Client, Storage, KeyProvider, Block: traits::Block> fmt::Debug for Offchain
}
}
impl<Client, Storage, KeyProvider, Block> OffchainWorkers<
impl<Client, Storage, Block> OffchainWorkers<
Client,
Storage,
KeyProvider,
Block,
> where
Block: traits::Block,
Client: ProvideRuntimeApi + Send + Sync + 'static,
Client::Api: OffchainWorkerApi<Block>,
KeyProvider: AuthorityKeyProvider<Block> + Send,
Storage: client::backend::OffchainStorage + 'static,
{
/// Start the offchain workers after given block.
@@ -152,8 +110,6 @@ impl<Client, Storage, KeyProvider, Block> 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<Client, Storage, KeyProvider, Block> 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<Block> {
_marker: PhantomData<Block>,
pub(crate) sr_key: Option<sr25519::Pair>,
pub(crate) ed_key: Option<ed25519::Pair>,
}
impl<Block: traits::Block> Default for TestProvider<Block> {
fn default() -> Self {
Self {
_marker: PhantomData,
sr_key: None,
ed_key: None,
}
}
}
impl<Block: traits::Block> AuthorityKeyProvider<Block> for TestProvider<Block> {
type ConsensusPair = ed25519::Pair;
type FinalityPair = sr25519::Pair;
fn authority_key(&self, _: &BlockId<Block>) -> Option<Self::ConsensusPair> {
self.ed_key.clone()
}
fn fg_authority_key(&self, _: &BlockId<Block>) -> Option<Self::FinalityPair> {
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();
-43
View File
@@ -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<Vec<u8>, ()> {
unimplemented!("not needed in tests so far")
}
fn new_crypto_key(&mut self, _crypto: CryptoKind) -> Result<CryptoKey, ()> {
unimplemented!("not needed in tests so far")
}
fn encrypt(
&mut self,
_key: CryptoKey,
_data: &[u8],
) -> Result<Vec<u8>, ()> {
unimplemented!("not needed in tests so far")
}
fn decrypt(
&mut self,
_key: CryptoKey,
_data: &[u8],
) -> Result<Vec<u8>, ()> {
unimplemented!("not needed in tests so far")
}
fn sign(
&mut self,
_key: CryptoKey,
_data: &[u8],
) -> Result<Vec<u8>, ()> {
unimplemented!("not needed in tests so far")
}
fn verify(
&mut self,
_key: CryptoKey,
_msg: &[u8],
_signature: &[u8],
) -> Result<bool, ()> {
unimplemented!("not needed in tests so far")
}
fn timestamp(&mut self) -> Timestamp {
unimplemented!("not needed in tests so far")
}
+1 -1
View File
@@ -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" }
+202 -36
View File
@@ -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<T: Zeroize> AsRef<T> for Protected<T> {
}
}
impl<T: Zeroize> rstd::ops::Deref for Protected<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
#[cfg(feature = "std")]
impl<T: Zeroize> std::fmt::Debug for Protected<T> {
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<Iter: Iterator<Item=DeriveJunction>>(&self, _path: Iter) -> Option<Self> {
None
}
@@ -457,8 +465,8 @@ impl<T: AsMut<[u8]> + 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<u8>` filled with raw data.
#[cfg(feature = "std")]
fn to_raw_vec(&self) -> Vec<u8>;
fn to_raw_vec(&self) -> Vec<u8> { 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<u8> { 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<Item=DeriveJunction>
>(&self, _: Iter) -> Result<Self, Self::DeriveError> { Ok(Self) }
fn from_seed(_: &Self::Seed) -> Self { Self }
fn from_seed_slice(_: &[u8]) -> Result<Self, SecretStringError> { Ok(Self) }
fn from_standard_components<
I: Iterator<Item=DeriveJunction>
>(
_: &str,
_: Option<&str>,
_: I
) -> Result<Self, SecretStringError> { Ok(Self) }
fn sign(&self, _: &[u8]) -> Self::Signature { Self }
fn verify<M: AsRef<[u8]>>(_: &Self::Signature, _: M, _: &Self::Public) -> bool { true }
fn verify_weak<P: AsRef<[u8]>, M: AsRef<[u8]>>(_: &[u8], _: M, _: P) -> bool { true }
fn public(&self) -> Self::Public { Self }
fn to_raw_vec(&self) -> Vec<u8> { 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<P: AsRef<Self::Public>, M: AsRef<[u8]>>(sig: &Self::Signature, message: M, pubkey: P) -> bool;
fn verify<M: AsRef<[u8]>>(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<P: AsRef<[u8]>, 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<u8>;
}
/// One type is wrapped by another.
pub trait IsWrappedBy<Outer>: From<Outer> + Into<Outer> {
/// 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<Self>;
}
impl<T, Outer> IsWrappedBy<Outer> for T where
Outer: AsRef<Self> + AsMut<Self> + From<Self>,
T: From<Outer>,
{
/// 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<Inner, Outer, T> UncheckedFrom<T> for Outer where
Outer: Wraps<Inner=Inner>,
Inner: IsWrappedBy<Outer> + UncheckedFrom<T>,
{
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.
impl From<u32> for KeyTypeId {
fn from(x: u32) -> Self {
Self(x.to_le_bytes())
}
}
impl From<KeyTypeId> for u32 {
fn from(x: KeyTypeId) -> Self {
u32::from_le_bytes(x.0)
}
}
impl<'a> TryFrom<&'a str> for KeyTypeId {
type Error = ();
fn try_from(x: &'a str) -> Result<Self, ()> {
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)
}
}
/// 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;
/// ED25519 public key.
pub const ED25519: KeyTypeId = 10;
/// SR25519 public key.
pub const SR25519: KeyTypeId = 20;
}
/// A trait for something that has a key type ID.
pub trait TypedKey {
/// The type ID of this key.
const KEY_TYPE: 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<String>, path: Vec<DeriveJunction>},
Seed(Vec<u8>),
}
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: &<TestPair as Pair>::Seed) -> Self { TestPair::Seed(vec![]) }
fn sign(&self, _message: &[u8]) -> Self::Signature { [] }
fn verify<P: AsRef<Self::Public>, M: AsRef<[u8]>>(
_sig: &Self::Signature,
_message: M,
_pubkey: P
) -> bool { true }
fn verify<M: AsRef<[u8]>>(_: &Self::Signature, _: M, _: &Self::Public) -> bool { true }
fn verify_weak<P: AsRef<[u8]>, 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() {
+52 -49
View File
@@ -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<Self, Self::Error> {
if data.len() == 32 {
let mut inner = [0u8; 32];
inner.copy_from_slice(data);
Ok(Public(inner))
} else {
Err(())
}
}
}
impl From<Public> for [u8; 32] {
fn from(x: Public) -> Self {
x.0
@@ -90,12 +104,6 @@ impl From<Pair> for Public {
}
}
impl AsRef<Public> for Public {
fn as_ref(&self) -> &Public {
&self
}
}
impl From<Public> for H256 {
fn from(x: Public) -> Self {
x.0.into()
@@ -115,15 +123,15 @@ impl UncheckedFrom<H256> 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<H: ::std::hash::Hasher>(&self, state: &mut H) {
impl std::hash::Hash for Public {
fn hash<H: std::hash::Hasher>(&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<Self, Self::Error> {
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<H: ::std::hash::Hasher>(&self, state: &mut H) {
::std::hash::Hash::hash(&self.0[..], state);
impl std::hash::Hash for Signature {
fn hash<H: std::hash::Hasher>(&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<u8>` filled with raw data.
#[cfg(feature = "std")]
fn to_raw_vec(&self) -> Vec<u8> {
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<Pair> 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<P: AsRef<Self::Public>, 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<M: AsRef<[u8]>>(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)]
+18 -3
View File
@@ -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<u8>`.
#[derive(PartialEq, Eq, Clone)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord))]
@@ -147,7 +161,7 @@ impl<R: codec::Encode> ::std::fmt::Debug for NativeOrEncoded<R> {
#[cfg(feature = "std")]
impl<R: codec::Encode> NativeOrEncoded<R> {
/// 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())
}
}
+2 -162
View File
@@ -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<StorageKind> 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<u32> for CryptoKind {
type Error = ();
fn try_from(kind: u32) -> Result<Self, Self::Error> {
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<CryptoKind> 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<u64> for CryptoKey {
type Error = ();
fn try_from(key: u64) -> Result<Self, Self::Error> {
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<CryptoKey> 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<OpaqueNetworkState, ()>;
/// 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<CryptoKey, ()>;
/// Returns the locally configured authority public key, if available.
fn pubkey(&self, key: CryptoKey) -> Result<Vec<u8>, ()>;
/// 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<Vec<u8>, ()>;
/// 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<Vec<u8>, ()>;
/// 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<Vec<u8>, ()>;
/// 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<bool, ()>;
/// Returns current UNIX timestamp (in millis)
fn timestamp(&mut self) -> Timestamp;
@@ -466,34 +353,10 @@ impl<T: Externalities + ?Sized> Externalities for Box<T> {
(&mut **self).submit_transaction(ex)
}
fn new_crypto_key(&mut self, crypto: CryptoKind) -> Result<CryptoKey, ()> {
(&mut **self).new_crypto_key(crypto)
}
fn encrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).encrypt(key, data)
}
fn network_state(&self) -> Result<OpaqueNetworkState, ()> {
(& **self).network_state()
}
fn pubkey(&self, key: CryptoKey) -> Result<Vec<u8>, ()> {
(&**self).pubkey(key)
}
fn decrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).decrypt(key, data)
}
fn sign(&mut self, key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).sign(key, data)
}
fn verify(&mut self, key: CryptoKey, msg: &[u8], signature: &[u8]) -> Result<bool, ()> {
(&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);
}
}
+55 -52
View File
@@ -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<Public> 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<Public> for H256 {
}
}
impl rstd::convert::TryFrom<&[u8]> for Public {
type Error = ();
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
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<H256> 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<H: ::std::hash::Hasher>(&self, state: &mut H) {
impl std::hash::Hash for Public {
fn hash<H: std::hash::Hasher>(&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<Self, Self::Error> {
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<Iter: Iterator<Item=DeriveJunction>>(&self, path: Iter) -> Option<Public> {
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<u8>` filled with raw data.
#[cfg(feature = "std")]
fn to_raw_vec(&self) -> Vec<u8> {
self.0.to_vec()
}
/// Return a slice filled with raw data.
fn as_slice(&self) -> &[u8] {
&self.0
}
}
#[cfg(feature = "std")]
impl AsRef<Pair> 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<P: AsRef<Self::Public>, 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<M: AsRef<[u8]>>(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<P: AsRef<[u8]>, 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));
}
}
+115
View File
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<KeyTypeId, std::collections::HashMap<Vec<u8>, Vec<u8>>>,
}
#[cfg(feature = "std")]
impl KeyStore {
/// Creates a new instance of `Self`.
pub fn new() -> std::sync::Arc<parking_lot::RwLock<Self>> {
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<sr25519::Public> {
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<sr25519::Public, String> {
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<sr25519::Pair> {
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<ed25519::Public> {
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<ed25519::Public, String> {
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<ed25519::Pair> {
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"))
)
}
}
+72
View File
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<sr25519::Public>;
/// 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<sr25519::Public, String>;
/// 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<sr25519::Pair>;
/// Returns all ed25519 public keys for the given key type.
fn ed25519_public_keys(&self, id: KeyTypeId) -> Vec<ed25519::Public>;
/// 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<ed25519::Public, String>;
/// 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<ed25519::Pair>;
/// 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<parking_lot::RwLock<dyn BareCryptoStore>>;
+3 -2
View File
@@ -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"
+12
View File
@@ -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 {
+77 -11
View File
@@ -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<Hash, BlockHash> {
#[rpc(name = "author_submitExtrinsic")]
fn submit_extrinsic(&self, extrinsic: Bytes) -> Result<Hash>;
/// Insert a key into the keystore.
#[rpc(name = "author_insertKey")]
fn insert_key(&self,
key_type: String,
suri: String,
maybe_public: Option<Bytes>
) -> Result<Bytes>;
/// Returns all pending extrinsics, potentially grouped by sender.
#[rpc(name = "author_pendingExtrinsics")]
fn pending_extrinsics(&self) -> Result<Vec<Bytes>>;
/// 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<hash::ExtrinsicOrHash<Hash>>) -> Result<Vec<Hash>>;
fn remove_extrinsic(&self,
bytes_or_hash: Vec<hash::ExtrinsicOrHash<Hash>>
) -> Result<Vec<Hash>>;
/// Submit an extrinsic to watch.
#[pubsub(subscription = "author_extrinsicUpdate", subscribe, name = "author_submitAndWatchExtrinsic")]
fn watch_extrinsic(&self, metadata: Self::Metadata, subscriber: Subscriber<Status<Hash, BlockHash>>, bytes: Bytes);
#[pubsub(
subscription = "author_extrinsicUpdate",
subscribe,
name = "author_submitAndWatchExtrinsic"
)]
fn watch_extrinsic(&self,
metadata: Self::Metadata,
subscriber: Subscriber<Status<Hash, BlockHash>>,
bytes: Bytes
);
/// Unsubscribe from extrinsic watching.
#[pubsub(subscription = "author_extrinsicUpdate", unsubscribe, name = "author_unwatchExtrinsic")]
fn unwatch_extrinsic(&self, metadata: Option<Self::Metadata>, id: SubscriptionId) -> Result<bool>;
#[pubsub(
subscription = "author_extrinsicUpdate",
unsubscribe,
name = "author_unwatchExtrinsic"
)]
fn unwatch_extrinsic(&self,
metadata: Option<Self::Metadata>,
id: SubscriptionId
) -> Result<bool>;
}
/// Authoring API
@@ -82,6 +110,8 @@ pub struct Author<B, E, P, RA> where P: PoolChainApi + Sync + Send + 'static {
pool: Arc<Pool<P>>,
/// Subscriptions manager
subscriptions: Subscriptions,
/// The key store.
keystore: BareCryptoStorePtr,
}
impl<B, E, P, RA> Author<B, E, P, RA> where P: PoolChainApi + Sync + Send + 'static {
@@ -90,11 +120,13 @@ impl<B, E, P, RA> Author<B, E, P, RA> where P: PoolChainApi + Sync + Send + 'sta
client: Arc<Client<B, E, <P as PoolChainApi>::Block, RA>>,
pool: Arc<Pool<P>>,
subscriptions: Subscriptions,
keystore: BareCryptoStorePtr,
) -> Self {
Author {
client,
pool,
subscriptions,
keystore,
}
}
}
@@ -105,10 +137,38 @@ impl<B, E, P, RA> AuthorApi<ExHash<P>, BlockHash<P>> for Author<B, E, P, RA> whe
P: PoolChainApi + Sync + Send + 'static,
P::Block: traits::Block<Hash=H256>,
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<Bytes>,
) -> Result<Bytes> {
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<ExHash<P>> {
let xt = Decode::decode(&mut &ext[..])?;
let best_block_hash = self.client.info().chain.best_hash;
@@ -124,7 +184,9 @@ impl<B, E, P, RA> AuthorApi<ExHash<P>, BlockHash<P>> for Author<B, E, P, RA> whe
Ok(self.pool.ready().map(|tx| tx.data.encode().into()).collect())
}
fn remove_extrinsic(&self, bytes_or_hash: Vec<hash::ExtrinsicOrHash<ExHash<P>>>) -> Result<Vec<ExHash<P>>> {
fn remove_extrinsic(&self,
bytes_or_hash: Vec<hash::ExtrinsicOrHash<ExHash<P>>>
) -> Result<Vec<ExHash<P>>> {
let hashes = bytes_or_hash.into_iter()
.map(|x| match x {
hash::ExtrinsicOrHash::Hash(h) => Ok(h),
@@ -143,7 +205,11 @@ impl<B, E, P, RA> AuthorApi<ExHash<P>, BlockHash<P>> for Author<B, E, P, RA> whe
)
}
fn watch_extrinsic(&self, _metadata: Self::Metadata, subscriber: Subscriber<Status<ExHash<P>, BlockHash<P>>>, xt: Bytes) {
fn watch_extrinsic(&self,
_metadata: Self::Metadata,
subscriber: Subscriber<Status<ExHash<P>, BlockHash<P>>>,
xt: Bytes
) {
let submit = || -> Result<_> {
let best_block_hash = self.client.info().chain.best_hash;
let dxt = <<P as PoolChainApi>::Block as traits::Block>::Extrinsic::decode(&mut &xt[..])?;
+53 -1
View File
@@ -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<KeyTypeId, HashMap<Vec<u8>, String>>,
}
impl BareCryptoStore for TestKeyStore {
fn sr25519_public_keys(&self, _id: KeyTypeId) -> Vec<sr25519::Public> { vec![] }
fn sr25519_generate_new(&mut self, _id: KeyTypeId, _seed: Option<&str>)
-> std::result::Result<sr25519::Public, String>
{
Err("unimplemented".into())
}
fn sr25519_key_pair(&self, _id: KeyTypeId, _pub_key: &sr25519::Public) -> Option<sr25519::Pair> {
None
}
fn ed25519_public_keys(&self, _id: KeyTypeId) -> Vec<ed25519::Public> { vec![] }
fn ed25519_generate_new(&mut self, _id: KeyTypeId, _seed: Option<&str>)
-> std::result::Result<ed25519::Public, String>
{
Err("unimplemented".into())
}
fn ed25519_key_pair(&self, _id: KeyTypeId, _pub_key: &ed25519::Public) -> Option<ed25519::Pair> {
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();
+6 -2
View File
@@ -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" }
+3 -3
View File
@@ -34,7 +34,7 @@ enum GenesisSource<G> {
Factory(fn() -> G),
}
impl<G: RuntimeGenesis> Clone for GenesisSource<G> {
impl<G> Clone for GenesisSource<G> {
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<String, json::Value>;
/// A configuration of a chain. Can be used to build a genesis block.
pub struct ChainSpec<G: RuntimeGenesis> {
pub struct ChainSpec<G> {
spec: ChainSpecFile,
genesis: GenesisSource<G>,
}
impl<G: RuntimeGenesis> Clone for ChainSpec<G> {
impl<G> Clone for ChainSpec<G> {
fn clone(&self) -> Self {
ChainSpec {
spec: self.spec.clone(),
+56 -32
View File
@@ -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<C> = <
/// Block type for `Components`
pub type ComponentBlock<C> = <<C as Components>::Factory as ServiceFactory>::Block;
/// ConsensusPair type for `Components`
pub type ComponentConsensusPair<C> = <<C as Components>::Factory as ServiceFactory>::ConsensusPair;
/// FinalityPair type for `Components`
pub type ComponentFinalityPair<C> = <<C as Components>::Factory as ServiceFactory>::FinalityPair;
/// AuthorityKeyProvider type for `Components`
pub type ComponentAuthorityKeyProvider<C> =
AuthorityKeyProvider<ComponentBlock<C>, ComponentConsensusPair<C>, ComponentFinalityPair<C>>;
/// Extrinsic hash type for `Components`
pub type ComponentExHash<C> = <<C as Components>::TransactionPoolApi as txpool::ChainApi>::Hash;
@@ -152,6 +145,27 @@ pub type PoolApi<C> = <C as Components>::TransactionPoolApi;
pub trait RuntimeGenesis: Serialize + DeserializeOwned + BuildStorage {}
impl<T: Serialize + DeserializeOwned + BuildStorage> RuntimeGenesis for T {}
/// Something that can create initial session keys from given seeds.
pub trait InitialSessionKeys<C: Components> {
/// Generate the initial session keys for the given seeds.
fn generate_intial_session_keys(
client: Arc<ComponentClient<C>>,
seeds: Vec<String>,
) -> error::Result<()>;
}
impl<C: Components> InitialSessionKeys<Self> for C where
ComponentClient<C>: ProvideRuntimeApi,
<ComponentClient<C> as ProvideRuntimeApi>::Api: substrate_session::SessionKeys<ComponentBlock<C>>,
{
fn generate_intial_session_keys(
client: Arc<ComponentClient<C>>,
seeds: Vec<String>,
) -> error::Result<()> {
substrate_session::generate_initial_session_keys(client, seeds).map_err(Into::into)
}
}
/// Something that can start the RPC service.
pub trait StartRPC<C: Components> {
fn start_rpc(
@@ -160,6 +174,7 @@ pub trait StartRPC<C: Components> {
system_info: SystemInfo,
task_executor: TaskExecutor,
transaction_pool: Arc<TransactionPool<C::TransactionPoolApi>>,
keystore: KeyStorePtr,
) -> rpc::RpcHandler;
}
@@ -173,11 +188,12 @@ impl<C: Components> StartRPC<Self> for C where
rpc_system_info: SystemInfo,
task_executor: TaskExecutor,
transaction_pool: Arc<TransactionPool<C::TransactionPoolApi>>,
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::<ComponentBlock<C>, ComponentExHash<C>, _, _, _, _>(
state,
@@ -242,7 +258,6 @@ pub trait OffchainWorker<C: Components> {
offchain: &offchain::OffchainWorkers<
ComponentClient<C>,
ComponentOffchainStorage<C>,
ComponentAuthorityKeyProvider<C>,
ComponentBlock<C>
>,
pool: &Arc<TransactionPool<C::TransactionPoolApi>>,
@@ -259,7 +274,6 @@ impl<C: Components> OffchainWorker<Self> for C where
offchain: &offchain::OffchainWorkers<
ComponentClient<C>,
ComponentOffchainStorage<C>,
ComponentAuthorityKeyProvider<C>,
ComponentBlock<C>
>,
pool: &Arc<TransactionPool<C::TransactionPoolApi>>,
@@ -277,6 +291,7 @@ pub trait ServiceTrait<C: Components>:
+ StartRPC<C>
+ MaintainTransactionPool<C>
+ OffchainWorker<C>
+ InitialSessionKeys<C>
{}
impl<C: Components, T> ServiceTrait<C> for T where
T: Deref<Target = Service<C>>
@@ -285,6 +300,7 @@ impl<C: Components, T> ServiceTrait<C> for T where
+ StartRPC<C>
+ MaintainTransactionPool<C>
+ OffchainWorker<C>
+ InitialSessionKeys<C>
{}
/// Alias for a an implementation of `futures::future::Executor`.
@@ -294,10 +310,6 @@ pub type TaskExecutor = Arc<dyn Executor<Box<dyn Future<Item = (), Error = ()> +
pub trait ServiceFactory: 'static + Sized {
/// Block type.
type Block: BlockT<Hash=H256>;
/// 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<Self::Factory>,
executor: CodeExecutor<Self::Factory>,
keystore: Option<BareCryptoStorePtr>,
) -> Result<
(
Arc<ComponentClient<Self>>,
@@ -498,11 +511,11 @@ impl<Factory: ServiceFactory> Components for FullComponents<Factory> {
fn build_client(
config: &FactoryFullConfiguration<Factory>,
executor: CodeExecutor<Self::Factory>,
)
-> Result<(
Arc<ComponentClient<Self>>,
Option<Arc<OnDemand<FactoryBlock<Self::Factory>>>>
), error::Error>
keystore: Option<BareCryptoStorePtr>,
) -> Result<
(Arc<ComponentClient<Self>>, Option<Arc<OnDemand<FactoryBlock<Self::Factory>>>>),
error::Error,
>
{
let db_settings = client_db::DatabaseSettings {
cache_size: config.database_cache_size.map(|u| u as usize),
@@ -512,12 +525,19 @@ impl<Factory: ServiceFactory> Components for FullComponents<Factory> {
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<Factory: ServiceFactory> Components for LightComponents<Factory> {
fn build_client(
config: &FactoryFullConfiguration<Factory>,
executor: CodeExecutor<Self::Factory>,
_: Option<BareCryptoStorePtr>,
)
-> Result<
(
@@ -615,9 +636,12 @@ impl<Factory: ServiceFactory> Components for LightComponents<Factory> {
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)?;
+11 -7
View File
@@ -31,7 +31,7 @@ use tel::TelemetryEndpoints;
/// Service configuration.
#[derive(Clone)]
pub struct Configuration<C, G: Serialize + DeserializeOwned + BuildStorage> {
pub struct Configuration<C, G> {
/// Implementation name
pub impl_name: &'static str,
/// Implementation version
@@ -45,7 +45,7 @@ pub struct Configuration<C, G: Serialize + DeserializeOwned + BuildStorage> {
/// Network configuration.
pub network: NetworkConfiguration,
/// Path to key files.
pub keystore_path: Option<PathBuf>,
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<C, G: Serialize + DeserializeOwned + BuildStorage> {
pub state_cache_child_ratio: Option<usize>,
/// Pruning settings.
pub pruning: PruningMode,
/// Additional key seeds.
pub keys: Vec<String>,
/// Chain configuration.
pub chain_spec: ChainSpec<G>,
/// Custom configuration.
@@ -91,7 +89,13 @@ pub struct Configuration<C, G: Serialize + DeserializeOwned + BuildStorage> {
/// 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<String>,
pub keystore_password: Option<Protected<String>>,
/// 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<String>,
}
impl<C: Default, G: Serialize + DeserializeOwned + BuildStorage> Configuration<C, G> {
@@ -111,7 +115,6 @@ impl<C: Default, G: Serialize + DeserializeOwned + BuildStorage> Configuration<C
database_cache_size: Default::default(),
state_cache_size: Default::default(),
state_cache_child_ratio: Default::default(),
keys: Default::default(),
custom: Default::default(),
pruning: PruningMode::default(),
execution_strategies: Default::default(),
@@ -126,7 +129,8 @@ impl<C: Default, G: Serialize + DeserializeOwned + BuildStorage> Configuration<C
force_authoring: false,
disable_grandpa: false,
grandpa_voter: false,
password: "".to_string().into(),
keystore_password: None,
dev_key_seed: None,
};
configuration.network.boot_nodes = configuration.chain_spec.boot_nodes().to_vec();
+34 -156
View File
@@ -26,7 +26,6 @@ pub mod chain_ops;
pub mod error;
use std::io;
use std::marker::PhantomData;
use std::net::SocketAddr;
use std::collections::HashMap;
use std::time::{Duration, Instant};
@@ -41,9 +40,8 @@ use keystore::Store as Keystore;
use network::{NetworkState, NetworkStateInfo};
use log::{log, info, warn, debug, error, Level};
use codec::{Encode, Decode};
use primitives::{Pair, ed25519, sr25519, crypto};
use sr_primitives::generic::BlockId;
use sr_primitives::traits::{Header, NumberFor, SaturatedConversion, Zero};
use sr_primitives::traits::{Header, NumberFor, SaturatedConversion};
use substrate_executor::NativeExecutor;
use sysinfo::{get_current_pid, ProcessExt, System, SystemExt};
use tel::{telemetry, SUBSTRATE_INFO};
@@ -57,13 +55,12 @@ pub use transaction_pool::txpool::{
pub use client::FinalityNotifications;
pub use components::{
ServiceFactory, FullBackend, FullExecutor, LightBackend, ComponentAuthorityKeyProvider,
ServiceFactory, FullBackend, FullExecutor, LightBackend,
LightExecutor, Components, PoolApi, ComponentClient, ComponentOffchainStorage,
ComponentBlock, FullClient, LightClient, FullComponents, LightComponents,
CodeExecutor, NetworkService, FactoryChainSpec, FactoryBlock,
FactoryFullConfiguration, RuntimeGenesis, FactoryGenesis,
ComponentExHash, ComponentExtrinsic, FactoryExtrinsic,
ComponentConsensusPair, ComponentFinalityPair,
ComponentExHash, ComponentExtrinsic, FactoryExtrinsic, InitialSessionKeys,
};
use components::{StartRPC, MaintainTransactionPool, OffchainWorker};
#[doc(hidden)]
@@ -85,8 +82,7 @@ pub struct Service<Components: components::Components> {
NetworkStatus<ComponentBlock<Components>>, NetworkState
)>>>>,
transaction_pool: Arc<TransactionPool<Components::TransactionPoolApi>>,
keystore: ComponentAuthorityKeyProvider<Components>,
exit: ::exit_future::Exit,
exit: exit_future::Exit,
signal: Option<Signal>,
/// Sender for futures that must be spawned as background tasks.
to_spawn_tx: mpsc::UnboundedSender<Box<dyn Future<Item = (), Error = ()> + Send>>,
@@ -105,21 +101,22 @@ pub struct Service<Components: components::Components> {
_offchain_workers: Option<Arc<offchain::OffchainWorkers<
ComponentClient<Components>,
ComponentOffchainStorage<Components>,
ComponentAuthorityKeyProvider<Components>,
ComponentBlock<Components>>
>>,
keystore: keystore::KeyStorePtr,
}
/// Creates bare client without any networking.
pub fn new_client<Factory: components::ServiceFactory>(config: &FactoryFullConfiguration<Factory>)
-> Result<Arc<ComponentClient<components::FullComponents<Factory>>>, error::Error>
{
pub fn new_client<Factory: components::ServiceFactory>(
config: &FactoryFullConfiguration<Factory>,
) -> Result<Arc<ComponentClient<components::FullComponents<Factory>>>, error::Error> {
let executor = NativeExecutor::new(config.default_heap_pages);
let (client, _) = components::FullComponents::<Factory>::build_client(
components::FullComponents::<Factory>::build_client(
config,
executor,
)?;
Ok(client)
None,
).map(|r| r.0)
}
/// An handle for spawning tasks in the service.
@@ -172,43 +169,9 @@ impl<Components: components::Components> Service<Components> {
// 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
};
let keystore = Keystore::open(config.keystore_path.clone(), config.keystore_password.clone())?;
// 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::<ed25519::Pair>(seed)?;
keystore.generate_from_seed::<sr25519::Pair>(seed)?;
}
public_key = match keystore.contents::<ed25519::Public>()?.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!("<disabled-keystore>");
}
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<Components: components::Components> Service<Components> {
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::<u64>(),
"best" => ?chain_info.best_hash
);
@@ -234,7 +204,7 @@ impl<Components: components::Components> Service<Components> {
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<Components: components::Components> Service<Components> {
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<Components: components::Components> Service<Components> {
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<Components: components::Components> Service<Components> {
"version" => version.clone(),
"config" => "",
"chain" => chain_name.clone(),
"pubkey" => &public_key,
"authority" => is_authority,
"network_id" => network_id.clone()
);
@@ -491,7 +449,6 @@ impl<Components: components::Components> Service<Components> {
to_spawn_tx,
to_spawn_rx,
to_poll: Vec::new(),
keystore: keystore_authority_key,
config,
exit,
rpc_handlers,
@@ -499,28 +456,20 @@ impl<Components: components::Components> Service<Components> {
_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<ComponentConsensusPair<Components>> {
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<ComponentFinalityPair<Components>> {
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<tel::Telemetry> {
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<Item = (), Error = ()> + Send + 'static) {
let _ = self.to_spawn_tx.unbounded_send(Box::new(task));
@@ -910,73 +859,6 @@ impl<C: Components> network::TransactionPool<ComponentExHash<C>, ComponentBlock<
}
}
#[derive(Clone)]
/// A provider of current authority key.
pub struct AuthorityKeyProvider<Block, ConsensusPair, FinalityPair> {
_marker: PhantomData<(Block, ConsensusPair, FinalityPair)>,
roles: Roles,
keystore: Option<Arc<Keystore>>,
password: crypto::Protected<String>,
}
impl<Block, ConsensusPair, FinalityPair>
offchain::AuthorityKeyProvider<Block>
for AuthorityKeyProvider<Block, ConsensusPair, FinalityPair>
where
Block: sr_primitives::traits::Block,
ConsensusPair: Pair,
FinalityPair: Pair,
{
type ConsensusPair = ConsensusPair;
type FinalityPair = FinalityPair;
fn authority_key(&self, _at: &BlockId<Block>) -> Option<Self::ConsensusPair> {
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<Block>) -> Option<Self::FinalityPair> {
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;
+3 -7
View File
@@ -135,10 +135,6 @@ fn node_config<F: ServiceFactory> (
) -> FactoryFullConfiguration<F>
{
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<F: ServiceFactory> (
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<F: ServiceFactory> (
force_authoring: false,
disable_grandpa: false,
grandpa_voter: false,
password: "".to_string().into(),
dev_key_seed: key_seed,
}
}
+15
View File
@@ -0,0 +1,15 @@
[package]
name = "substrate-session"
version = "2.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
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" ]
+64
View File
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<u8>>) -> Vec<u8>;
}
}
/// Generate the initial session keys with the given seeds.
#[cfg(feature = "std")]
pub fn generate_initial_session_keys<B, E, Block, RA>(
client: std::sync::Arc<client::Client<B, E, Block, RA>>,
seeds: Vec<String>,
) -> Result<(), client::error::Error>
where
B: client::backend::Backend<Block, Blake2Hasher>,
E: client::CallExecutor<Block, Blake2Hasher>,
Block: BlockT<Hash=H256>,
client::Client<B, E, Block, RA>: ProvideRuntimeApi,
<client::Client<B, E, Block, RA> as ProvideRuntimeApi>::Api: SessionKeys<Block>,
{
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(())
}
@@ -190,5 +190,6 @@ fn record_proof_works() {
&executor,
"Core_execute_block",
&block.encode(),
None,
).expect("Executes block while using the proof backend");
}
+43 -46
View File
@@ -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<P: AsRef<[u8]>>(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<ed25519::Public>;
/// 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<M: AsRef<[u8]>>(
id: KeyTypeId,
pubkey: &ed25519::Public,
msg: &M,
) -> Option<ed25519::Signature>;
/// 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<sr25519::Public>;
/// 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<M: AsRef<[u8]>>(
id: KeyTypeId,
pubkey: &sr25519::Public,
msg: &M,
) -> Option<sr25519::Signature>;
/// Verify an sr25519 signature.
fn sr25519_verify<P: AsRef<[u8]>>(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<OpaqueNetworkState, ()>;
/// Returns the currently configured authority public key, if available.
fn pubkey(key: CryptoKey) -> Result<Vec<u8>, ()>;
/// 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<CryptoKey, ()>;
/// 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<Vec<u8>, ()>;
/// 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<Vec<u8>, ()>;
/// 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<Vec<u8>, ()>;
/// 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<bool, ()>;
/// Returns current UNIX timestamp (in millis)
fn timestamp() -> Timestamp;
+77 -61
View File
@@ -15,24 +15,19 @@
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
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<Blake2Hasher>);
@@ -208,12 +203,82 @@ impl OtherApi for () {
}
impl CryptoApi for () {
fn ed25519_verify<P: AsRef<[u8]>>(sig: &[u8; 64], msg: &[u8], pubkey: P) -> bool {
ed25519::Pair::verify_weak(sig, msg, pubkey)
fn ed25519_public_keys(id: KeyTypeId) -> Vec<ed25519::Public> {
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 sr25519_verify<P: AsRef<[u8]>>(sig: &[u8; 64], msg: &[u8], pubkey: P) -> bool {
sr25519::Pair::verify_weak(sig, msg, pubkey)
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<M: AsRef<[u8]>>(
id: KeyTypeId,
pubkey: &ed25519::Public,
msg: &M,
) -> Option<ed25519::Signature> {
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<sr25519::Public> {
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<M: AsRef<[u8]>>(
id: KeyTypeId,
pubkey: &sr25519::Public,
msg: &M,
) -> Option<sr25519::Signature> {
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: &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<Vec<u8>, ()> {
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<offchain::CryptoKey, ()> {
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<Vec<u8>, ()> {
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<Vec<u8>, ()> {
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<Vec<u8>, ()> {
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<bool, ()> {
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()
+149 -197
View File
@@ -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;
/// Note: ext_sr25519_verify returns 0 if the signature is correct, nonzero otherwise.
/// 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;
/// 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<PublicKey encoded to Vec<u8>, ()>`.
/// `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<P: AsRef<[u8]>>(sig: &[u8; 64], msg: &[u8], pubkey: P) -> bool {
fn ed25519_public_keys(id: KeyTypeId) -> Vec<ed25519::Public> {
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<P: AsRef<[u8]>>(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<M: AsRef<[u8]>>(
id: KeyTypeId,
pubkey: &ed25519::Public,
msg: &M,
) -> Option<ed25519::Signature> {
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<sr25519::Public> {
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<M: AsRef<[u8]>>(
id: KeyTypeId,
pubkey: &sr25519::Public,
msg: &M,
) -> Option<sr25519::Signature> {
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<Vec<u8>, ()> {
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<offchain::CryptoKey, ()> {
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<Vec<u8>, ()> {
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<Vec<u8>, ()> {
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<Vec<u8>, ()> {
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<bool, ()> {
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()()
+4
View File
@@ -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",
]
@@ -45,11 +45,10 @@ for
where
AccountId: Member + MaybeDisplay,
Call: Member + Dispatchable<Origin=Origin>,
Extra: SignedExtension<AccountId=AccountId>,
Extra: SignedExtension<AccountId=AccountId, Call=Call>,
Origin: From<Option<AccountId>>,
{
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<DispatchResult, DispatchError> {
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)))
@@ -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(()) }
}
+13 -4
View File
@@ -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<L: Lazy<[u8]>>(&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)
}
}
+60 -23
View File
@@ -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<AuthorityId> for UintAuthorityId {
fn into(self) -> AuthorityId {
impl UintAuthorityId {
/// Convert this authority id into a public key.
pub fn to_public_key<T: Public>(&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::<u64>())
}
}
}
impl app_crypto::RuntimeAppPublic for UintAuthorityId {
type Signature = u64;
fn all() -> Vec<Self> {
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<M: AsRef<[u8]>>(&self, msg: &M) -> Option<Self::Signature> {
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<M: AsRef<[u8]>>(&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<std::slice::Iter<'static, KeyTypeId>>;
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<Xt> traits::Extrinsic for ExtrinsicWrapper<Xt> {
}
}
impl<Xt: Encode> serde::Serialize for ExtrinsicWrapper<Xt>
{
impl<Xt: Encode> serde::Serialize for ExtrinsicWrapper<Xt> {
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer {
self.using_encoded(|bytes| seq.serialize_bytes(bytes))
}
@@ -249,13 +286,13 @@ impl<Call: Codec + Sync + Send, Extra> traits::Extrinsic for TestXt<Call, Extra>
impl<Origin, Call, Extra> Applyable for TestXt<Call, Extra> where
Call: 'static + Sized + Send + Sync + Clone + Eq + Codec + Debug + Dispatchable<Origin=Origin>,
Extra: SignedExtension<AccountId=u64>,
Extra: SignedExtension<AccountId=u64, Call=Call>,
Origin: From<Option<u64>>
{
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<U: ValidateUnsigned<Call=Self::Call>>(&self,
@@ -272,10 +309,10 @@ impl<Origin, Call, Extra> Applyable for TestXt<Call, Extra> where
len: usize,
) -> Result<DispatchResult, DispatchError> {
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()))
+55 -27
View File
@@ -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<L: Lazy<[u8]>>(&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<L: Lazy<[u8]>>(&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<DispatchError> 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<Self::AdditionalSigned, &'static str>;
/// 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<ValidTransaction, DispatchError> { Ok(Default::default()) }
) -> Result<ValidTransaction, DispatchError> {
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<ValidTransaction, DispatchError> { 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<AccountId=AccountId>),+
Call,
$($direct: SignedExtension<AccountId=AccountId, Call=Call>),+
> SignedExtension for ($($direct),+,) {
type AccountId = AccountId;
type Call = Call;
type AdditionalSigned = ($($direct::AdditionalSigned,)+);
fn additional_signed(&self) -> Result<Self::AdditionalSigned, &'static str> {
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<ValidTransaction, DispatchError> {
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<ValidTransaction, DispatchError> {
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<u8> {
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(), )*
_ => &[],
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
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"] }
+1 -1
View File
@@ -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"
+6 -1
View File
@@ -168,7 +168,12 @@ impl<H: Hasher> Externalities<H> 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<primitives::traits::BareCryptoStorePtr> {
warn!("Call to non-existent keystore.");
None
}
}
+21 -14
View File
@@ -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<BareCryptoStorePtr>,
/// Dummy usage of N arg.
_phantom: ::std::marker::PhantomData<N>,
}
@@ -103,6 +104,7 @@ where
backend: &'a B,
changes_trie_storage: Option<&'a T>,
offchain_externalities: Option<&'a mut O>,
keystore: Option<BareCryptoStorePtr>,
) -> 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<BareCryptoStorePtr> {
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()),
);
}
}
+85 -88
View File
@@ -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<H: Hasher> {
/// Returns offchain externalities extension if present.
fn offchain(&mut self) -> Option<&mut dyn offchain::Externalities>;
/// Returns the keystore.
fn keystore(&self) -> Option<BareCryptoStorePtr>;
}
/// 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<Vec<u8>, ()> {
unreachable!()
}
fn new_crypto_key(
&mut self,
_crypto: offchain::CryptoKind,
) -> Result<offchain::CryptoKey, ()> {
unreachable!()
}
fn encrypt(
&mut self,
_key: offchain::CryptoKey,
_data: &[u8],
) -> Result<Vec<u8>, ()> {
unreachable!()
}
fn decrypt(
&mut self,
_key: offchain::CryptoKey,
_data: &[u8],
) -> Result<Vec<u8>, ()> {
unreachable!()
}
fn sign(
&mut self,
_key: offchain::CryptoKey,
_data: &[u8],
) -> Result<Vec<u8>, ()> {
unreachable!()
}
fn verify(
&mut self,
_key: offchain::CryptoKey,
_msg: &[u8],
_signature: &[u8],
) -> Result<bool, ()> {
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<BareCryptoStorePtr>,
) -> 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<BareCryptoStorePtr>,
_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<B, H, Exec>(
exec: &Exec,
method: &str,
call_data: &[u8],
keystore: Option<BareCryptoStorePtr>,
) -> Result<(Vec<u8>, Vec<Vec<u8>>), Box<dyn Error>>
where
B: Backend<H>,
@@ -716,7 +679,7 @@ where
{
let trie_backend = backend.as_trie_backend()
.ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box<dyn Error>)?;
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<S, H, Exec>(
exec: &Exec,
method: &str,
call_data: &[u8],
keystore: Option<BareCryptoStorePtr>,
) -> Result<(Vec<u8>, Vec<Vec<u8>>), Box<dyn Error>>
where
S: trie_backend_essence::TrieBackendStorage<H>,
@@ -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<H, Exec>(
exec: &Exec,
method: &str,
call_data: &[u8],
keystore: Option<BareCryptoStorePtr>,
) -> Result<Vec<u8>, Box<dyn Error>>
where
H: Hasher,
@@ -776,7 +742,7 @@ where
H::Out: Ord + 'static,
{
let trie_backend = create_proof_check_backend::<H>(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<H, Exec>(
exec: &Exec,
method: &str,
call_data: &[u8],
keystore: Option<BareCryptoStorePtr>,
) -> Result<Vec<u8>, Box<dyn Error>>
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::<Blake2Hasher, _>(remote_root, remote_proof,
&mut Default::default(), &executor, "test", &[]).unwrap();
let local_result = execution_proof_check::<Blake2Hasher, _>(
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::<Blake2Hasher, u64>::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::<Blake2Hasher, u64>::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::<Blake2Hasher, u64>::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::<Blake2Hasher, u64>::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::<Blake2Hasher, u64>::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()
);
}
}
@@ -371,6 +371,7 @@ mod tests {
&backend,
Some(&changes_trie_storage),
crate::NeverOffchainExt::new(),
None,
);
const ROOT: [u8; 32] = hex!("39245109cef3758c2eed2ccba8d9b370a917850af3824bc8348d505df2c298fa");
+9 -2
View File
@@ -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<H: Hasher, N: ChangesTrieBlockNumber> {
backend: InMemory<H>,
changes_trie_storage: ChangesTrieInMemoryStorage<H, N>,
offchain: Option<Box<dyn offchain::Externalities>>,
keystore: Option<BareCryptoStorePtr>,
}
impl<H: Hasher, N: ChangesTrieBlockNumber> TestExternalities<H, N> {
@@ -84,6 +86,7 @@ impl<H: Hasher, N: ChangesTrieBlockNumber> TestExternalities<H, N> {
changes_trie_storage: ChangesTrieInMemoryStorage::new(),
backend: backend.into(),
offchain: None,
keystore: None,
}
}
@@ -263,6 +266,10 @@ impl<H, N> Externalities<H> for TestExternalities<H, N>
.as_mut()
.map(|x| &mut **x as _)
}
fn keystore(&self) -> Option<BareCryptoStorePtr> {
self.keystore.clone()
}
}
#[cfg(test)]
+1 -1
View File
@@ -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"

Some files were not shown because too many files have changed in this diff Show More