From 4f4a0c5b38ff3e1c6358cb1068e4cd7af8dafa29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= <bkchr@users.noreply.github.com> Date: Mon, 22 Feb 2021 15:24:09 +0100 Subject: [PATCH] Make keystore return `None` when a key doesn't exist (#8163) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Make keystore return `None` when a key doesn't exist * Fixes * More fixes * Update comment * Update primitives/keystore/src/lib.rs Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * Update client/keystore/src/local.rs Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * Address comments * Update client/keystore/src/local.rs Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> --- substrate/bin/node/cli/src/service.rs | 4 +- .../client/authority-discovery/src/worker.rs | 6 +- .../authority-discovery/src/worker/tests.rs | 6 +- substrate/client/consensus/aura/src/lib.rs | 7 +- .../client/consensus/babe/src/authorship.rs | 4 +- substrate/client/consensus/babe/src/lib.rs | 3 + substrate/client/keystore/src/lib.rs | 4 - substrate/client/keystore/src/local.rs | 130 ++++++++++++------ .../primitives/finality-grandpa/src/lib.rs | 2 +- substrate/primitives/io/src/lib.rs | 9 +- substrate/primitives/keystore/src/lib.rs | 69 +++++----- substrate/primitives/keystore/src/testing.rs | 48 ++++--- 12 files changed, 174 insertions(+), 118 deletions(-) diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index db4ed3f4f1d..312a0226fc3 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -625,9 +625,7 @@ mod tests { sp_consensus_babe::AuthorityId::ID, &alice.to_public_crypto_pair(), &to_sign, - ).unwrap() - .try_into() - .unwrap(); + ).unwrap().unwrap().try_into().unwrap(); let item = <DigestItem as CompatibleDigestItem>::babe_seal( signature, ); diff --git a/substrate/client/authority-discovery/src/worker.rs b/substrate/client/authority-discovery/src/worker.rs index dac7a97746b..b1fb89669bf 100644 --- a/substrate/client/authority-discovery/src/worker.rs +++ b/substrate/client/authority-discovery/src/worker.rs @@ -296,10 +296,10 @@ where for (sign_result, key) in signatures.into_iter().zip(keys) { let mut signed_addresses = vec![]; - // sign_with_all returns Result<Signature, Error> signature - // is generated for a public key that is supported. // Verify that all signatures exist for all provided keys. - let signature = sign_result.map_err(|_| Error::MissingSignature(key.clone()))?; + let signature = sign_result.ok() + .flatten() + .ok_or_else(|| Error::MissingSignature(key.clone()))?; schema::SignedAuthorityAddresses { addresses: serialized_addresses.clone(), signature, diff --git a/substrate/client/authority-discovery/src/worker/tests.rs b/substrate/client/authority-discovery/src/worker/tests.rs index a994e08691b..04f597aa26b 100644 --- a/substrate/client/authority-discovery/src/worker/tests.rs +++ b/substrate/client/authority-discovery/src/worker/tests.rs @@ -187,7 +187,7 @@ async fn build_dht_event( serialized_addresses.as_slice(), ) .await - .map_err(|_| Error::Signing) + .unwrap() .unwrap(); let mut signed_addresses = vec![]; @@ -195,9 +195,7 @@ async fn build_dht_event( addresses: serialized_addresses.clone(), signature, } - .encode(&mut signed_addresses) - .map_err(Error::EncodingProto) - .unwrap(); + .encode(&mut signed_addresses).unwrap(); let key = hash_authority_id(&public_key.to_raw_vec()); let value = signed_addresses; diff --git a/substrate/client/consensus/aura/src/lib.rs b/substrate/client/consensus/aura/src/lib.rs index 0702ccd7f13..47ce364cb66 100644 --- a/substrate/client/consensus/aura/src/lib.rs +++ b/substrate/client/consensus/aura/src/lib.rs @@ -255,8 +255,8 @@ where let expected_author = slot_author::<P>(slot, epoch_data); expected_author.and_then(|p| { if SyncCryptoStore::has_keys( - &*self.keystore, - &[(p.to_raw_vec(), sp_application_crypto::key_types::AURA)], + &*self.keystore, + &[(p.to_raw_vec(), sp_application_crypto::key_types::AURA)], ) { Some(p.clone()) } else { @@ -299,6 +299,9 @@ where header_hash.as_ref() ).map_err(|e| sp_consensus::Error::CannotSign( public.clone(), e.to_string(), + ))? + .ok_or_else(|| sp_consensus::Error::CannotSign( + public.clone(), "Could not find key in keystore.".into(), ))?; let signature = signature.clone().try_into() .map_err(|_| sp_consensus::Error::InvalidSignature( diff --git a/substrate/client/consensus/babe/src/authorship.rs b/substrate/client/consensus/babe/src/authorship.rs index 1120f660613..cf75a4a43f2 100644 --- a/substrate/client/consensus/babe/src/authorship.rs +++ b/substrate/client/consensus/babe/src/authorship.rs @@ -159,7 +159,7 @@ fn claim_secondary_slot( authority_id.as_ref(), transcript_data, ); - if let Ok(signature) = result { + if let Ok(Some(signature)) = result { Some(PreDigest::SecondaryVRF(SecondaryVRFPreDigest { slot, vrf_output: VRFOutput(signature.output), @@ -265,7 +265,7 @@ fn claim_primary_slot( authority_id.as_ref(), transcript_data, ); - if let Ok(signature) = result { + if let Ok(Some(signature)) = result { let public = PublicKey::from_bytes(&authority_id.to_raw_vec()).ok()?; let inout = match signature.output.attach_input_hash(&public, transcript) { Ok(inout) => inout, diff --git a/substrate/client/consensus/babe/src/lib.rs b/substrate/client/consensus/babe/src/lib.rs index a6530dea08d..a8e533d2a83 100644 --- a/substrate/client/consensus/babe/src/lib.rs +++ b/substrate/client/consensus/babe/src/lib.rs @@ -649,6 +649,9 @@ where ) .map_err(|e| sp_consensus::Error::CannotSign( public.clone(), e.to_string(), + ))? + .ok_or_else(|| sp_consensus::Error::CannotSign( + public.clone(), "Could not find key in keystore.".into(), ))?; let signature: AuthoritySignature = signature.clone().try_into() .map_err(|_| sp_consensus::Error::InvalidSignature( diff --git a/substrate/client/keystore/src/lib.rs b/substrate/client/keystore/src/lib.rs index 9cad56efacf..38ab640d2e3 100644 --- a/substrate/client/keystore/src/lib.rs +++ b/substrate/client/keystore/src/lib.rs @@ -46,9 +46,6 @@ pub enum Error { /// Public key type is not supported #[display(fmt="Key crypto type is not supported")] KeyNotSupported(KeyTypeId), - /// Pair not found for public key and KeyTypeId - #[display(fmt="Pair not found for {} public key", "_0")] - PairNotFound(String), /// Keystore unavailable #[display(fmt="Keystore unavailable")] Unavailable, @@ -61,7 +58,6 @@ impl From<Error> for TraitError { fn from(error: Error) -> Self { match error { Error::KeyNotSupported(id) => TraitError::KeyNotSupported(id), - Error::PairNotFound(e) => TraitError::PairNotFound(e), Error::InvalidSeed | Error::InvalidPhrase | Error::InvalidPassword => { TraitError::ValidationError(error.to_string()) }, diff --git a/substrate/client/keystore/src/local.rs b/substrate/client/keystore/src/local.rs index 866a50ae4c9..482ef407601 100644 --- a/substrate/client/keystore/src/local.rs +++ b/substrate/client/keystore/src/local.rs @@ -60,9 +60,9 @@ impl LocalKeystore { /// Get a key pair for the given public key. /// - /// This function is only available for a local keystore. If your application plans to work with - /// remote keystores, you do not want to depend on it. - pub fn key_pair<Pair: AppPair>(&self, public: &<Pair as AppKey>::Public) -> Result<Pair> { + /// Returns `Ok(None)` if the key doesn't exist, `Ok(Some(_))` if the key exists and + /// `Err(_)` when something failed. + pub fn key_pair<Pair: AppPair>(&self, public: &<Pair as AppKey>::Public) -> Result<Option<Pair>> { self.0.read().key_pair::<Pair>(public) } } @@ -130,7 +130,7 @@ impl CryptoStore for LocalKeystore { id: KeyTypeId, key: &CryptoTypePublicPair, msg: &[u8], - ) -> std::result::Result<Vec<u8>, TraitError> { + ) -> std::result::Result<Option<Vec<u8>>, TraitError> { SyncCryptoStore::sign_with(self, id, key, msg) } @@ -139,7 +139,7 @@ impl CryptoStore for LocalKeystore { key_type: KeyTypeId, public: &sr25519::Public, transcript_data: VRFTranscriptData, - ) -> std::result::Result<VRFSignature, TraitError> { + ) -> std::result::Result<Option<VRFSignature>, TraitError> { SyncCryptoStore::sr25519_vrf_sign(self, key_type, public, transcript_data) } } @@ -175,28 +175,28 @@ impl SyncCryptoStore for LocalKeystore { id: KeyTypeId, key: &CryptoTypePublicPair, msg: &[u8], - ) -> std::result::Result<Vec<u8>, TraitError> { + ) -> std::result::Result<Option<Vec<u8>>, TraitError> { match key.0 { ed25519::CRYPTO_ID => { let pub_key = ed25519::Public::from_slice(key.1.as_slice()); - let key_pair: ed25519::Pair = self.0.read() + let key_pair = self.0.read() .key_pair_by_type::<ed25519::Pair>(&pub_key, id) .map_err(|e| TraitError::from(e))?; - Ok(key_pair.sign(msg).encode()) + key_pair.map(|k| k.sign(msg).encode()).map(Ok).transpose() } sr25519::CRYPTO_ID => { let pub_key = sr25519::Public::from_slice(key.1.as_slice()); - let key_pair: sr25519::Pair = self.0.read() + let key_pair = self.0.read() .key_pair_by_type::<sr25519::Pair>(&pub_key, id) .map_err(|e| TraitError::from(e))?; - Ok(key_pair.sign(msg).encode()) + key_pair.map(|k| k.sign(msg).encode()).map(Ok).transpose() }, ecdsa::CRYPTO_ID => { let pub_key = ecdsa::Public::from_slice(key.1.as_slice()); - let key_pair: ecdsa::Pair = self.0.read() + let key_pair = self.0.read() .key_pair_by_type::<ecdsa::Pair>(&pub_key, id) .map_err(|e| TraitError::from(e))?; - Ok(key_pair.sign(msg).encode()) + key_pair.map(|k| k.sign(msg).encode()).map(Ok).transpose() } _ => Err(TraitError::KeyNotSupported(id)) } @@ -232,7 +232,7 @@ impl SyncCryptoStore for LocalKeystore { .map(|k| ed25519::Public::from_slice(k.as_slice())) .collect() }) - .unwrap_or_default() + .unwrap_or_default() } fn ed25519_generate_new( @@ -278,7 +278,8 @@ impl SyncCryptoStore for LocalKeystore { } fn has_keys(&self, public_keys: &[(Vec<u8>, KeyTypeId)]) -> bool { - public_keys.iter().all(|(p, t)| self.0.read().key_phrase_by_type(&p, *t).is_ok()) + public_keys.iter() + .all(|(p, t)| self.0.read().key_phrase_by_type(&p, *t).ok().flatten().is_some()) } fn sr25519_vrf_sign( @@ -286,16 +287,19 @@ impl SyncCryptoStore for LocalKeystore { key_type: KeyTypeId, public: &Sr25519Public, transcript_data: VRFTranscriptData, - ) -> std::result::Result<VRFSignature, TraitError> { + ) -> std::result::Result<Option<VRFSignature>, TraitError> { let transcript = make_transcript(transcript_data); - let pair = self.0.read().key_pair_by_type::<Sr25519Pair>(public, key_type) - .map_err(|e| TraitError::PairNotFound(e.to_string()))?; + let pair = self.0.read().key_pair_by_type::<Sr25519Pair>(public, key_type)?; - let (inout, proof, _) = pair.as_ref().vrf_sign(transcript); - Ok(VRFSignature { - output: inout.to_output(), - proof, - }) + if let Some(pair) = pair { + let (inout, proof, _) = pair.as_ref().vrf_sign(transcript); + Ok(Some(VRFSignature { + output: inout.to_output(), + proof, + })) + } else { + Ok(None) + } } } @@ -411,36 +415,53 @@ impl KeystoreInner { } /// Get the key phrase for a given public key and key type. - fn key_phrase_by_type(&self, public: &[u8], key_type: KeyTypeId) -> Result<String> { + fn key_phrase_by_type(&self, public: &[u8], key_type: KeyTypeId) -> Result<Option<String>> { if let Some(phrase) = self.get_additional_pair(public, key_type) { - return Ok(phrase.clone()) + return Ok(Some(phrase.clone())) } - let path = self.key_file_path(public, key_type).ok_or_else(|| Error::Unavailable)?; - let file = File::open(path)?; + let path = if let Some(path) = self.key_file_path(public, key_type) { + path + } else { + return Ok(None); + }; + + if path.exists() { + let file = File::open(path)?; - serde_json::from_reader(&file).map_err(Into::into) + serde_json::from_reader(&file).map_err(Into::into).map(Some) + } else { + Ok(None) + } } /// Get a key pair for the given public key and key type. - fn key_pair_by_type<Pair: PairT>(&self, + fn key_pair_by_type<Pair: PairT>( + &self, public: &Pair::Public, key_type: KeyTypeId, - ) -> Result<Pair> { - let phrase = self.key_phrase_by_type(public.as_slice(), key_type)?; + ) -> Result<Option<Pair>> { + let phrase = if let Some(p) = self.key_phrase_by_type(public.as_slice(), key_type)? { + p + } else { + return Ok(None) + }; + let pair = Pair::from_string( &phrase, self.password(), ).map_err(|_| Error::InvalidPhrase)?; if &pair.public() == public { - Ok(pair) + Ok(Some(pair)) } else { Err(Error::InvalidPassword) } } - /// Returns the file path for the given public key and key type. + /// Get the file path for the given public key and key type. + /// + /// Returns `None` if the keystore only exists in-memory and there isn't any path to provide. fn key_file_path(&self, public: &[u8], key_type: KeyTypeId) -> Option<PathBuf> { let mut buf = self.path.as_ref()?.clone(); let key_type = hex::encode(key_type.0); @@ -481,8 +502,12 @@ impl KeystoreInner { } /// 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) + /// + /// Returns `Ok(None)` if the key doesn't exist, `Ok(Some(_))` if the key exists or `Err(_)` when + /// something failed. + pub fn key_pair<Pair: AppPair>(&self, public: &<Pair as AppKey>::Public) -> Result<Option<Pair>> { + self.key_pair_by_type::<Pair::Generic>(IsWrappedBy::from_ref(public), Pair::ID) + .map(|v| v.map(Into::into)) } } @@ -531,13 +556,40 @@ mod tests { assert!(store.public_keys::<ed25519::AppPublic>().unwrap().is_empty()); let key: ed25519::AppPair = store.generate().unwrap(); - let key2: ed25519::AppPair = store.key_pair(&key.public()).unwrap(); + let key2: ed25519::AppPair = store.key_pair(&key.public()).unwrap().unwrap(); assert_eq!(key.public(), key2.public()); assert_eq!(store.public_keys::<ed25519::AppPublic>().unwrap()[0], key.public()); } + #[test] + fn has_keys_works() { + let temp_dir = TempDir::new().unwrap(); + let store = LocalKeystore::open(temp_dir.path(), None).unwrap(); + + let key: ed25519::AppPair = store.0.write().generate().unwrap(); + let key2 = ed25519::Pair::generate().0; + + assert!( + !SyncCryptoStore::has_keys(&store, &[(key2.public().to_vec(), ed25519::AppPublic::ID)]) + ); + + assert!( + !SyncCryptoStore::has_keys( + &store, + &[ + (key2.public().to_vec(), ed25519::AppPublic::ID), + (key.public().to_raw_vec(), ed25519::AppPublic::ID), + ], + ) + ); + + assert!( + SyncCryptoStore::has_keys(&store, &[(key.public().to_raw_vec(), ed25519::AppPublic::ID)]) + ); + } + #[test] fn test_insert_ephemeral_from_seed() { let temp_dir = TempDir::new().unwrap(); @@ -554,7 +606,7 @@ mod tests { drop(store); let store = KeystoreInner::open(temp_dir.path(), None).unwrap(); // Keys generated from seed should not be persisted! - assert!(store.key_pair::<ed25519::AppPair>(&pair.public()).is_err()); + assert!(store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().is_none()); } #[test] @@ -569,7 +621,7 @@ mod tests { let pair: ed25519::AppPair = store.generate().unwrap(); assert_eq!( pair.public(), - store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().public(), + store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().unwrap().public(), ); // Without the password the key should not be retrievable @@ -582,7 +634,7 @@ mod tests { ).unwrap(); assert_eq!( pair.public(), - store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().public(), + store.key_pair::<ed25519::AppPair>(&pair.public()).unwrap().unwrap().public(), ); } @@ -626,7 +678,7 @@ mod tests { let store_key_pair = store.key_pair_by_type::<sr25519::AppPair>( &key_pair.public(), SR25519, - ).expect("Gets key pair from keystore"); + ).expect("Gets key pair from keystore").unwrap(); assert_eq!(key_pair.public(), store_key_pair.public()); } diff --git a/substrate/primitives/finality-grandpa/src/lib.rs b/substrate/primitives/finality-grandpa/src/lib.rs index 383e4fe3713..5b393bd1d80 100644 --- a/substrate/primitives/finality-grandpa/src/lib.rs +++ b/substrate/primitives/finality-grandpa/src/lib.rs @@ -400,7 +400,7 @@ where AuthorityId::ID, &public.to_public_crypto_pair(), &encoded[..], - ).ok()?.try_into().ok()?; + ).ok().flatten()?.try_into().ok()?; Some(grandpa::SignedMessage { message, diff --git a/substrate/primitives/io/src/lib.rs b/substrate/primitives/io/src/lib.rs index 397dd3c2171..c0db1120dc4 100644 --- a/substrate/primitives/io/src/lib.rs +++ b/substrate/primitives/io/src/lib.rs @@ -474,8 +474,9 @@ pub trait Crypto { let keystore = &***self.extension::<KeystoreExt>() .expect("No `keystore` associated for the current context!"); SyncCryptoStore::sign_with(keystore, id, &pub_key.into(), msg) - .map(|sig| ed25519::Signature::from_slice(sig.as_slice())) .ok() + .flatten() + .map(|sig| ed25519::Signature::from_slice(sig.as_slice())) } /// Verify `ed25519` signature. @@ -600,8 +601,9 @@ pub trait Crypto { let keystore = &***self.extension::<KeystoreExt>() .expect("No `keystore` associated for the current context!"); SyncCryptoStore::sign_with(keystore, id, &pub_key.into(), msg) - .map(|sig| sr25519::Signature::from_slice(sig.as_slice())) .ok() + .flatten() + .map(|sig| sr25519::Signature::from_slice(sig.as_slice())) } /// Verify an `sr25519` signature. @@ -646,8 +648,9 @@ pub trait Crypto { let keystore = &***self.extension::<KeystoreExt>() .expect("No `keystore` associated for the current context!"); SyncCryptoStore::sign_with(keystore, id, &pub_key.into(), msg) - .map(|sig| ecdsa::Signature::from_slice(sig.as_slice())) .ok() + .flatten() + .map(|sig| ecdsa::Signature::from_slice(sig.as_slice())) } /// Verify `ecdsa` signature. diff --git a/substrate/primitives/keystore/src/lib.rs b/substrate/primitives/keystore/src/lib.rs index f42f6dd7122..2fda3a48c5d 100644 --- a/substrate/primitives/keystore/src/lib.rs +++ b/substrate/primitives/keystore/src/lib.rs @@ -34,9 +34,6 @@ pub enum Error { /// Public key type is not supported #[display(fmt="Key not supported: {:?}", _0)] KeyNotSupported(KeyTypeId), - /// Pair not found for public key and KeyTypeId - #[display(fmt="Pair was not found: {}", _0)] - PairNotFound(String), /// Validation error #[display(fmt="Validation error: {}", _0)] ValidationError(String), @@ -125,37 +122,39 @@ pub trait CryptoStore: Send + Sync { /// Signs a message with the private key that matches /// the public key passed. /// - /// Returns the SCALE encoded signature if key is found & supported, - /// an error otherwise. + /// Returns the SCALE encoded signature if key is found and supported, `None` if the key doesn't + /// exist or an error when something failed. async fn sign_with( &self, id: KeyTypeId, key: &CryptoTypePublicPair, msg: &[u8], - ) -> Result<Vec<u8>, Error>; + ) -> Result<Option<Vec<u8>>, Error>; /// Sign with any key /// /// Given a list of public keys, find the first supported key and /// sign the provided message with that key. /// - /// Returns a tuple of the used key and the SCALE encoded signature. + /// Returns a tuple of the used key and the SCALE encoded signature or `None` if no key could + /// be found to sign. async fn sign_with_any( &self, id: KeyTypeId, keys: Vec<CryptoTypePublicPair>, msg: &[u8] - ) -> Result<(CryptoTypePublicPair, Vec<u8>), Error> { + ) -> Result<Option<(CryptoTypePublicPair, Vec<u8>)>, Error> { if keys.len() == 1 { - return self.sign_with(id, &keys[0], msg).await.map(|s| (keys[0].clone(), s)); + return Ok(self.sign_with(id, &keys[0], msg).await?.map(|s| (keys[0].clone(), s))); } else { for k in self.supported_keys(id, keys).await? { - if let Ok(sign) = self.sign_with(id, &k, msg).await { - return Ok((k, sign)); + if let Ok(Some(sign)) = self.sign_with(id, &k, msg).await { + return Ok(Some((k, sign))); } } } - Err(Error::KeyNotSupported(id)) + + Ok(None) } /// Sign with all keys @@ -164,13 +163,13 @@ pub trait CryptoStore: Send + Sync { /// each key given that the key is supported. /// /// Returns a list of `Result`s each representing the SCALE encoded - /// signature of each key or a Error for non-supported keys. + /// signature of each key, `None` if the key doesn't exist or a error when something failed. async fn sign_with_all( &self, id: KeyTypeId, keys: Vec<CryptoTypePublicPair>, msg: &[u8], - ) -> Result<Vec<Result<Vec<u8>, Error>>, ()> { + ) -> Result<Vec<Result<Option<Vec<u8>>, Error>>, ()> { let futs = keys.iter() .map(|k| self.sign_with(id, k, msg)); @@ -187,16 +186,14 @@ pub trait CryptoStore: Send + Sync { /// Namely, VRFOutput and VRFProof which are returned /// inside the `VRFSignature` container struct. /// - /// This function will return an error in the cases where - /// the public key and key type provided do not match a private - /// key in the keystore. Or, in the context of remote signing - /// an error could be a network one. + /// This function will return `None` if the given `key_type` and `public` combination + /// doesn't exist in the keystore or an `Err` when something failed. async fn sr25519_vrf_sign( &self, key_type: KeyTypeId, public: &sr25519::Public, transcript_data: VRFTranscriptData, - ) -> Result<VRFSignature, Error>; + ) -> Result<Option<VRFSignature>, Error>; } /// Sync version of the CryptoStore @@ -285,37 +282,41 @@ pub trait SyncCryptoStore: CryptoStore + Send + Sync { /// Signs a message with the private key that matches /// the public key passed. /// - /// Returns the SCALE encoded signature if key is found & supported, - /// an error otherwise. + /// Returns the SCALE encoded signature if key is found and supported, `None` if the key doesn't + /// exist or an error when something failed. fn sign_with( &self, id: KeyTypeId, key: &CryptoTypePublicPair, msg: &[u8], - ) -> Result<Vec<u8>, Error>; + ) -> Result<Option<Vec<u8>>, Error>; /// Sign with any key /// /// Given a list of public keys, find the first supported key and /// sign the provided message with that key. /// - /// Returns a tuple of the used key and the SCALE encoded signature. + /// Returns a tuple of the used key and the SCALE encoded signature or `None` if no key could + /// be found to sign. fn sign_with_any( &self, id: KeyTypeId, keys: Vec<CryptoTypePublicPair>, msg: &[u8] - ) -> Result<(CryptoTypePublicPair, Vec<u8>), Error> { + ) -> Result<Option<(CryptoTypePublicPair, Vec<u8>)>, Error> { if keys.len() == 1 { - return SyncCryptoStore::sign_with(self, id, &keys[0], msg).map(|s| (keys[0].clone(), s)); + return Ok( + SyncCryptoStore::sign_with(self, id, &keys[0], msg)?.map(|s| (keys[0].clone(), s)), + ) } else { for k in SyncCryptoStore::supported_keys(self, id, keys)? { - if let Ok(sign) = SyncCryptoStore::sign_with(self, id, &k, msg) { - return Ok((k, sign)); + if let Ok(Some(sign)) = SyncCryptoStore::sign_with(self, id, &k, msg) { + return Ok(Some((k, sign))); } } } - Err(Error::KeyNotSupported(id)) + + Ok(None) } /// Sign with all keys @@ -324,13 +325,13 @@ pub trait SyncCryptoStore: CryptoStore + Send + Sync { /// each key given that the key is supported. /// /// Returns a list of `Result`s each representing the SCALE encoded - /// signature of each key or a Error for non-supported keys. + /// signature of each key, `None` if the key doesn't exist or an error when something failed. fn sign_with_all( &self, id: KeyTypeId, keys: Vec<CryptoTypePublicPair>, msg: &[u8], - ) -> Result<Vec<Result<Vec<u8>, Error>>, ()>{ + ) -> Result<Vec<Result<Option<Vec<u8>>, Error>>, ()> { Ok(keys.iter().map(|k| SyncCryptoStore::sign_with(self, id, k, msg)).collect()) } @@ -344,16 +345,14 @@ pub trait SyncCryptoStore: CryptoStore + Send + Sync { /// Namely, VRFOutput and VRFProof which are returned /// inside the `VRFSignature` container struct. /// - /// This function will return an error in the cases where - /// the public key and key type provided do not match a private - /// key in the keystore. Or, in the context of remote signing - /// an error could be a network one. + /// This function will return `None` if the given `key_type` and `public` combination + /// doesn't exist in the keystore or an `Err` when something failed. fn sr25519_vrf_sign( &self, key_type: KeyTypeId, public: &sr25519::Public, transcript_data: VRFTranscriptData, - ) -> Result<VRFSignature, Error>; + ) -> Result<Option<VRFSignature>, Error>; } /// A pointer to a keystore. diff --git a/substrate/primitives/keystore/src/testing.rs b/substrate/primitives/keystore/src/testing.rs index 702e2bbc857..caee7178e09 100644 --- a/substrate/primitives/keystore/src/testing.rs +++ b/substrate/primitives/keystore/src/testing.rs @@ -132,7 +132,7 @@ impl CryptoStore for KeyStore { id: KeyTypeId, key: &CryptoTypePublicPair, msg: &[u8], - ) -> Result<Vec<u8>, Error> { + ) -> Result<Option<Vec<u8>>, Error> { SyncCryptoStore::sign_with(self, id, key, msg) } @@ -141,7 +141,7 @@ impl CryptoStore for KeyStore { key_type: KeyTypeId, public: &sr25519::Public, transcript_data: VRFTranscriptData, - ) -> Result<VRFSignature, Error> { + ) -> Result<Option<VRFSignature>, Error> { SyncCryptoStore::sr25519_vrf_sign(self, key_type, public, transcript_data) } } @@ -280,27 +280,27 @@ impl SyncCryptoStore for KeyStore { id: KeyTypeId, key: &CryptoTypePublicPair, msg: &[u8], - ) -> Result<Vec<u8>, Error> { + ) -> Result<Option<Vec<u8>>, Error> { use codec::Encode; match key.0 { ed25519::CRYPTO_ID => { - let key_pair: ed25519::Pair = self - .ed25519_key_pair(id, &ed25519::Public::from_slice(key.1.as_slice())) - .ok_or_else(|| Error::PairNotFound("ed25519".to_owned()))?; - return Ok(key_pair.sign(msg).encode()); + let key_pair = self + .ed25519_key_pair(id, &ed25519::Public::from_slice(key.1.as_slice())); + + key_pair.map(|k| k.sign(msg).encode()).map(Ok).transpose() } sr25519::CRYPTO_ID => { - let key_pair: sr25519::Pair = self - .sr25519_key_pair(id, &sr25519::Public::from_slice(key.1.as_slice())) - .ok_or_else(|| Error::PairNotFound("sr25519".to_owned()))?; - return Ok(key_pair.sign(msg).encode()); + let key_pair = self + .sr25519_key_pair(id, &sr25519::Public::from_slice(key.1.as_slice())); + + key_pair.map(|k| k.sign(msg).encode()).map(Ok).transpose() } ecdsa::CRYPTO_ID => { - let key_pair: ecdsa::Pair = self - .ecdsa_key_pair(id, &ecdsa::Public::from_slice(key.1.as_slice())) - .ok_or_else(|| Error::PairNotFound("ecdsa".to_owned()))?; - return Ok(key_pair.sign(msg).encode()); + let key_pair = self + .ecdsa_key_pair(id, &ecdsa::Public::from_slice(key.1.as_slice())); + + key_pair.map(|k| k.sign(msg).encode()).map(Ok).transpose() } _ => Err(Error::KeyNotSupported(id)) } @@ -311,15 +311,19 @@ impl SyncCryptoStore for KeyStore { key_type: KeyTypeId, public: &sr25519::Public, transcript_data: VRFTranscriptData, - ) -> Result<VRFSignature, Error> { + ) -> Result<Option<VRFSignature>, Error> { let transcript = make_transcript(transcript_data); - let pair = self.sr25519_key_pair(key_type, public) - .ok_or_else(|| Error::PairNotFound("Not found".to_owned()))?; + let pair = if let Some(k) = self.sr25519_key_pair(key_type, public) { + k + } else { + return Ok(None) + }; + let (inout, proof, _) = pair.as_ref().vrf_sign(transcript); - Ok(VRFSignature { + Ok(Some(VRFSignature { output: inout.to_output(), proof, - }) + })) } } @@ -394,7 +398,7 @@ mod tests { &key_pair.public(), transcript_data.clone(), ); - assert!(result.is_err()); + assert!(result.unwrap().is_none()); SyncCryptoStore::insert_unknown( &store, @@ -410,6 +414,6 @@ mod tests { transcript_data, ); - assert!(result.is_ok()); + assert!(result.unwrap().is_some()); } } -- GitLab