diff --git a/primitives/io/src/lib.rs b/primitives/io/src/lib.rs index 59d1c4f37ef27..598c7a5e4b409 100644 --- a/primitives/io/src/lib.rs +++ b/primitives/io/src/lib.rs @@ -1402,6 +1402,43 @@ mod tests { ); assert!(!crypto::finish_batch_verify()); + + // 1 valid ecdsa signature + crypto::start_batch_verify(); + + let pair = ecdsa::Pair::generate_with_phrase(None).0; + let msg = b"ECDSA!!1"; + let signature = pair.sign(msg); + crypto::ecdsa_batch_verify(&signature, msg, &pair.public()); + + assert!(crypto::finish_batch_verify()); + + // 1 invalid ecdsa signature + crypto::start_batch_verify(); + + crypto::ecdsa_batch_verify( + &Default::default(), + &Vec::new(), + &Default::default(), + ); + + assert!(!crypto::finish_batch_verify()); + + // 1 valid, 1 invalid ecdsa signature + crypto::start_batch_verify(); + + let pair = ecdsa::Pair::generate_with_phrase(None).0; + let msg = b"ECDSA!!1"; + let signature = pair.sign(msg); + crypto::ecdsa_batch_verify(&signature, msg, &pair.public()); + + crypto::ecdsa_batch_verify( + &Default::default(), + &Vec::new(), + &Default::default(), + ); + + assert!(!crypto::finish_batch_verify()); }); } } diff --git a/primitives/runtime/src/generic/unchecked_extrinsic.rs b/primitives/runtime/src/generic/unchecked_extrinsic.rs index d16d404ddfd0e..6a9e5c3e69b3e 100644 --- a/primitives/runtime/src/generic/unchecked_extrinsic.rs +++ b/primitives/runtime/src/generic/unchecked_extrinsic.rs @@ -115,7 +115,7 @@ for where Address: Member + MaybeDisplay, Call: Encode + Member, - Signature: Member + traits::Verify, + Signature: Member + traits::BatchVerify, ::Signer: IdentifyAccount, Extra: SignedExtension, AccountId: Member + MaybeDisplay, @@ -128,7 +128,7 @@ where Some((signed, signature, extra)) => { let signed = lookup.lookup(signed)?; let raw_payload = SignedPayload::new(self.function, extra)?; - if !raw_payload.using_encoded(|payload| signature.verify(payload, &signed)) { + if !raw_payload.using_encoded(|payload| signature.batch_verify(payload, &signed)) { return Err(InvalidTransaction::BadProof.into()) } diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 5d65c13c664b7..be5d4de749e1d 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -92,7 +92,7 @@ pub use either::Either; /// bypasses this problem. pub type Justification = Vec; -use traits::{Verify, Lazy}; +use traits::{Verify, BatchVerify, Lazy}; /// A module identifier. These are per module and should be stored in a registry somewhere. #[derive(Clone, Copy, Eq, PartialEq, Encode, Decode)] @@ -340,6 +340,24 @@ impl Verify for MultiSignature { } } +impl BatchVerify for MultiSignature { + fn batch_verify>(&self, mut msg: L, signer: &AccountId32) -> bool { + match (self, signer) { + (MultiSignature::Ed25519(ref sig), who) => sig.batch_verify(msg, &ed25519::Public::from_slice(who.as_ref())), + (MultiSignature::Sr25519(ref sig), who) => sig.batch_verify(msg, &sr25519::Public::from_slice(who.as_ref())), + (MultiSignature::Ecdsa(ref sig), who) => { + let m = sp_io::hashing::blake2_256(msg.get()); + match sp_io::crypto::secp256k1_ecdsa_recover_compressed(sig.as_ref(), &m) { + Ok(pubkey) => + &sp_io::hashing::blake2_256(pubkey.as_ref()) + == >::as_ref(who), + _ => false, + } + } + } + } +} + /// Signature verify that can work with any known signature types.. #[derive(Eq, PartialEq, Clone, Default, Encode, Decode, RuntimeDebug)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] @@ -867,6 +885,28 @@ mod tests { assert!(multi_sig.verify(msg, &multi_signer.into_account())); } + #[test] + fn ecdsa_batch_verify_works() { + let mut ext = sp_state_machine::BasicExternalities::default(); + ext.register_extension( + sp_core::traits::TaskExecutorExt::new(sp_core::testing::TaskExecutor::new()), + ); + + ext.execute_with(move || { + let msg = &b"test-message"[..]; + let (pair, _) = ecdsa::Pair::generate(); + + let signature = pair.sign(&msg); + + let multi_sig = MultiSignature::from(signature.clone()); + let multi_signer = MultiSigner::from(pair.public()); + + let batching = SignatureBatching::start(); + assert!(signature.batch_verify(msg, &pair.public())); + assert!(multi_sig.batch_verify(msg, &multi_signer.into_account())); + assert!(batching.verify()); + }); + } #[test] #[should_panic(expected = "Signature verification has not been called")] diff --git a/primitives/runtime/src/testing.rs b/primitives/runtime/src/testing.rs index eefb36ae82779..5a34d1cad1a38 100644 --- a/primitives/runtime/src/testing.rs +++ b/primitives/runtime/src/testing.rs @@ -153,6 +153,12 @@ impl traits::Verify for TestSignature { } } +impl traits::BatchVerify for TestSignature { + fn batch_verify>(&self, msg: L, signer: &u64) -> bool { + traits::Verify::verify(self, msg, signer) + } +} + /// Digest item pub type DigestItem = generic::DigestItem; @@ -161,7 +167,7 @@ pub type Digest = generic::Digest; /// Block Header pub type Header = generic::Header; - + impl Header { /// A new header with the given number and default hash for all other fields. pub fn new_from_number(number: ::Number) -> Self { diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index 4d2b1f062f716..4700cb5c4350f 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -84,10 +84,24 @@ pub trait Verify { type Signer: IdentifyAccount; /// Verify a signature. /// - /// Return `true` if signature is valid for the value. + /// Should return `true` if signature is valid for the value. fn verify>(&self, msg: L, signer: &::AccountId) -> bool; } +/// Means of signature batch verification. +/// +/// Should only be used when there is a batching context registered on the host (through instantiating +/// helper struct `SingatureBatching` or via 'crypto::start_batch_verify`). +/// +/// If there were at least one call to this function, `crypto::finish_batch_verify` (or `SingatureBatching::verify`) +/// then should be called and code must panic if either returns false. +pub trait BatchVerify: Verify { + /// Verify a signature using available batcher. + /// + /// Should return `false` if batching failed or a previous signature already evaluated as invalid. + fn batch_verify>(&self, msg: L, signer: &::AccountId) -> bool; +} + impl Verify for sp_core::ed25519::Signature { type Signer = sp_core::ed25519::Public; @@ -96,6 +110,13 @@ impl Verify for sp_core::ed25519::Signature { } } + +impl BatchVerify for sp_core::ed25519::Signature { + fn batch_verify>(&self, mut msg: L, signer: &sp_core::ed25519::Public) -> bool { + sp_io::crypto::ed25519_batch_verify(self, msg.get(), signer) + } +} + impl Verify for sp_core::sr25519::Signature { type Signer = sp_core::sr25519::Public; @@ -104,6 +125,12 @@ impl Verify for sp_core::sr25519::Signature { } } +impl BatchVerify for sp_core::sr25519::Signature { + fn batch_verify>(&self, mut msg: L, signer: &sp_core::sr25519::Public) -> bool { + sp_io::crypto::sr25519_batch_verify(self, msg.get(), signer) + } +} + impl Verify for sp_core::ecdsa::Signature { type Signer = sp_core::ecdsa::Public; fn verify>(&self, mut msg: L, signer: &sp_core::ecdsa::Public) -> bool { @@ -117,6 +144,16 @@ impl Verify for sp_core::ecdsa::Signature { } } +impl BatchVerify for sp_core::ecdsa::Signature { + fn batch_verify>(&self, mut msg: L, signer: &sp_core::ecdsa::Public) -> bool { + sp_io::crypto::ecdsa_batch_verify( + self, + msg.get(), + signer, + ) + } +} + /// Means of signature verification of an application key. pub trait AppVerify { /// Type of the signer.