diff --git a/Cargo.lock b/Cargo.lock index f72287f0446..485dd47e276 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -519,12 +519,6 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d6f2aa4d0537bcc1c74df8755072bd31c1ef1a3a1b85a68e8404a8c353b7b8b" -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "core-foundation" version = "0.9.2" @@ -874,10 +868,8 @@ version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "convert_case", "proc-macro2", "quote", - "rustc_version", "syn", ] @@ -1450,6 +1442,9 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] [[package]] name = "hex-literal" @@ -1743,7 +1738,6 @@ name = "iroha_crypto" version = "2.0.0-pre.1" dependencies = [ "derive_more", - "eyre", "hex", "hex-literal", "iroha_schema", diff --git a/core/src/block.rs b/core/src/block.rs index 51405d21157..85ad19fc184 100644 --- a/core/src/block.rs +++ b/core/src/block.rs @@ -378,7 +378,7 @@ impl VersionedValidBlock { wsv: &WorldStateView, is_instruction_allowed: &IsInstructionAllowedBoxed, is_query_allowed: &IsQueryAllowedBoxed, - ) -> VersionedValidBlock { + ) -> Self { self.into_v1() .revalidate(wsv, is_instruction_allowed, is_query_allowed) .into() @@ -392,14 +392,12 @@ impl VersionedValidBlock { /// Sign this block and get [`VersionedValidBlock`](`Self`). /// # Errors /// Look at [`ValidBlock`](`ValidBlock`) for more info - pub fn sign(self, key_pair: KeyPair) -> Result { + pub fn sign(self, key_pair: KeyPair) -> Result { self.into_v1().sign(key_pair).map(Into::into) } /// Signatures that are verified with the `hash` of this block as `payload`. - pub fn verified_signatures( - &'_ self, - ) -> impl Iterator> + '_ { + pub fn verified_signatures(&self) -> impl Iterator> { self.as_v1() .verified_signatures() .map(SignatureOf::transmute_ref) @@ -473,13 +471,17 @@ impl ValidBlock { /// Commit block to the store. //TODO: pass block store and block sender as parameters? pub fn commit(self) -> CommittedBlock { + #[allow(clippy::expect_used)] + let signatures: SignaturesOf<_> = self + .signatures + .try_into() + .expect("Expected at least one signature"); + CommittedBlock { header: self.header, rejected_transactions: self.rejected_transactions, transactions: self.transactions, - signatures: SignaturesOf::from_iter_unchecked( - self.signatures.into_iter().map(SignatureOf::transmute), - ), + signatures: signatures.transmute(), } } @@ -489,8 +491,8 @@ impl ValidBlock { wsv: &WorldStateView, is_instruction_allowed: &IsInstructionAllowedBoxed, is_query_allowed: &IsQueryAllowedBoxed, - ) -> ValidBlock { - ValidBlock { + ) -> Self { + Self { signatures: self.signatures, ..ChainedBlock { header: self.header, @@ -515,7 +517,7 @@ impl ValidBlock { /// /// # Errors /// Fails if generating signature fails - pub fn sign(mut self, key_pair: KeyPair) -> Result { + pub fn sign(mut self, key_pair: KeyPair) -> Result { self.signatures.insert( SignatureOf::from_hash(key_pair, &self.hash()).wrap_err("Failed to sign block")?, ); @@ -523,7 +525,7 @@ impl ValidBlock { } /// Signatures that are verified with the `hash` of this block as `payload`. - pub fn verified_signatures(&'_ self) -> impl Iterator> + '_ { + pub fn verified_signatures(&self) -> impl Iterator> { let hash = self.hash(); self.signatures .iter() @@ -553,7 +555,7 @@ impl ValidBlock { #[cfg(test)] #[allow(clippy::restriction)] pub fn new_dummy() -> Self { - ValidBlock { + Self { header: BlockHeader { timestamp: 0, height: 1, @@ -655,7 +657,7 @@ impl VersionedCommittedBlock { } /// Signatures that are verified with the `hash` of this block as `payload`. - pub fn verified_signatures(&'_ self) -> impl Iterator> + '_ { + pub fn verified_signatures(&self) -> impl Iterator> { self.as_v1() .verified_signatures() .map(SignatureOf::transmute_ref) @@ -685,7 +687,7 @@ impl CommittedBlock { } /// Signatures that are verified with the `hash` of this block as `payload`. - pub fn verified_signatures(&'_ self) -> impl Iterator> + '_ { + pub fn verified_signatures(&self) -> impl Iterator> { self.signatures.verified_by_hash(self.hash()) } } @@ -699,12 +701,11 @@ impl From for ValidBlock { signatures, }: CommittedBlock, ) -> Self { - let signatures = signatures.into_iter().map(SignatureOf::transmute).collect(); Self { header, rejected_transactions, transactions, - signatures, + signatures: signatures.transmute().into(), } } } diff --git a/core/src/queue.rs b/core/src/queue.rs index 9344efed9eb..02c7b85b055 100644 --- a/core/src/queue.rs +++ b/core/src/queue.rs @@ -147,7 +147,7 @@ impl Queue { .get_mut() .as_mut_v1() .signatures - .merge(tx.into_v1().signatures); + .extend(tx.as_v1().signatures.clone()); return Ok(()); } Entry::Vacant(entry) => entry, diff --git a/core/src/sumeragi/mod.rs b/core/src/sumeragi/mod.rs index 5ab26806834..c7626dbd885 100644 --- a/core/src/sumeragi/mod.rs +++ b/core/src/sumeragi/mod.rs @@ -773,7 +773,8 @@ impl Entry::Occupied(mut occupied) => { let proof_votes = occupied.get_mut(); let count = proof_votes.signatures().len(); - proof_votes.merge_signatures(&proof); + proof_votes.merge_signatures(proof.signatures().clone()); + if proof_votes.signatures().len() > count { (true, proof_votes.clone()) } else { @@ -794,7 +795,7 @@ impl let (count_increased, merged_proof) = self.merge_view_change_votes(proof.clone()).await; if count_increased { self.broadcast_msg(ViewChangeSuggested::new( - proof.clone(), + proof, self.view_change_proofs().clone(), )) .await; @@ -857,17 +858,20 @@ impl trace!(?event); drop(self.events_sender.send(event)); } + let signed_block = block.sign(self.key_pair.clone())?; if !network_topology.is_consensus_required() { - self.commit_block(block).await; + self.commit_block(signed_block).await; return Ok(()); } - let voting_block = VotingBlock::new(block.clone()); - self.voting_block = Some(voting_block.clone()); - self.broadcast_msg(BlockCreated::from(block.sign(self.key_pair.clone())?)) + let voting_block = VotingBlock::new(signed_block.clone()); + let voting_block_hash = voting_block.block.hash(); + + self.voting_block = Some(voting_block); + self.broadcast_msg(BlockCreated::from(signed_block.clone())) .await; self.start_commit_countdown( - voting_block.clone(), + voting_block_hash, *self.latest_block_hash(), self.latest_view_change_hash(), ctx, @@ -878,16 +882,15 @@ impl /// Starts countdown for a period in which the `voting_block` should be committed. #[iroha_futures::telemetry_future] - #[log(skip(self, voting_block))] + #[log(skip(self, voting_block_hash))] #[allow(clippy::expect_used)] pub async fn start_commit_countdown( &self, - voting_block: VotingBlock, + voting_block_hash: HashOf, latest_block: HashOf, latest_view_change: HashOf, ctx: &mut Context, ) { - let voting_block_hash = voting_block.block.hash(); let proof = view_change::Proof::commit_timeout( voting_block_hash, latest_view_change, @@ -1387,10 +1390,12 @@ pub mod message { //TODO: send to set b so they can observe } let voting_block = VotingBlock::new(self.block.clone()); - sumeragi.voting_block = Some(voting_block.clone()); + let voting_block_hash = voting_block.block.hash(); + sumeragi.voting_block = Some(voting_block); + sumeragi .start_commit_countdown( - voting_block, + voting_block_hash, *sumeragi.latest_block_hash(), sumeragi.latest_view_change_hash(), ctx, diff --git a/core/src/sumeragi/view_change.rs b/core/src/sumeragi/view_change.rs index cf742ec192d..810c6c95da0 100644 --- a/core/src/sumeragi/view_change.rs +++ b/core/src/sumeragi/view_change.rs @@ -91,15 +91,14 @@ impl Proof { /// Can fail during creation of signature pub fn sign(mut self, key_pair: KeyPair) -> Result { let signature = SignatureOf::new(key_pair, &self.payload)?.transmute(); - self.signatures.add(signature); + self.signatures.insert(signature); Ok(self) } /// Adds verified signatures of `other` to self. - pub fn merge_signatures(&mut self, other: &Proof) { - self.signatures.merge(SignaturesOf::from_iter_unchecked( - other.signatures.verified_by_hash(self.hash()).cloned(), - )); + pub fn merge_signatures(&mut self, other: SignaturesOf) { + self.signatures + .extend(other.into_verified_by_hash(self.hash())) } /// Verify if the proof is valid, given the peers in `topology`. diff --git a/core/src/tx.rs b/core/src/tx.rs index 9f791581dd9..14d9c4bb292 100644 --- a/core/src/tx.rs +++ b/core/src/tx.rs @@ -134,7 +134,12 @@ impl AcceptedTransaction { transaction .check_instruction_len(max_instruction_number) .wrap_err("Failed to accept transaction")?; - let signatures = SignaturesOf::from_iter(&transaction.payload, transaction.signatures) + let signatures: SignaturesOf<_> = transaction + .signatures + .try_into() + .map_err(eyre::Error::from)?; + signatures + .verify(&transaction.payload) .wrap_err("Failed to verify transaction signatures")?; Ok(Self { diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 79d9b17bb7b..4dd2ed7ec7d 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -6,16 +6,18 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -iroha_schema = { path = "../schema"} +[features] +default = ["std"] +std = ["ursa"] -derive_more = "0.99.16" -ursa = "=0.3.7" -eyre = "0.6.5" -hex = "0.4.0" +[dependencies] +iroha_schema = { path = "../schema" } -parity-scale-codec = { version = "2.3.1", features = ["derive"] } -serde = { version = "1.0.130", features = ["derive"] } +derive_more = { version = "0.99.16", default-features = false, features = ["deref", "deref_mut", "display"] } +parity-scale-codec = { version = "2.3.1" , default-features = false, features = ["derive", "full"] } +serde = { version = "1.0.130", default-features = false, features = ["derive"] } +hex = { version = "0.4.0", default-features = false, features = ["alloc", "serde"] } +ursa = { version = "=0.3.7", optional = true } [dev-dependencies] hex-literal = "0.3.4" diff --git a/crypto/src/hash.rs b/crypto/src/hash.rs index a6a4f7d1df8..c774cc940f8 100644 --- a/crypto/src/hash.rs +++ b/crypto/src/hash.rs @@ -1,4 +1,6 @@ -use std::{ +#[cfg(not(feature = "std"))] +use alloc::{format, string::String, vec::Vec}; +use core::{ fmt::{self, Debug, Display, Formatter}, hash, marker::PhantomData, @@ -8,6 +10,7 @@ use derive_more::{Deref, DerefMut, Display}; use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "std")] use ursa::blake2::{ digest::{Update, VariableOutput}, VarBlake2b, @@ -15,17 +18,17 @@ use ursa::blake2::{ /// Hash of Iroha entities. Currently supports only blake2b-32. #[derive( + Clone, + Copy, + Hash, Eq, PartialEq, - Clone, - Encode, - Decode, - Serialize, - Deserialize, Ord, PartialOrd, - Copy, - Hash, + Decode, + Encode, + Deserialize, + Serialize, IntoSchema, )] pub struct Hash(pub [u8; Self::LENGTH]); @@ -35,6 +38,7 @@ impl Hash { pub const LENGTH: usize = 32; /// new hash from bytes + #[cfg(feature = "std")] #[allow(clippy::expect_used)] pub fn new(bytes: &[u8]) -> Self { let vec_hash = VarBlake2b::new(Self::LENGTH) @@ -69,24 +73,23 @@ impl AsRef<[u8]> for Hash { } /// Represents hash of Iroha entities like `Block` or `Transaction`. Currently supports only blake2b-32. -#[derive(Debug, Encode, Decode, Serialize, Deserialize, Deref, DerefMut, Display)] +// Lint triggers when expanding #[codec(skip)] +#[allow(clippy::default_trait_access)] +#[derive(Deref, DerefMut, Display, Decode, Encode, Deserialize, Serialize)] #[display(fmt = "{}", _0)] +#[serde(transparent)] pub struct HashOf( #[deref] #[deref_mut] Hash, - PhantomData, + #[codec(skip)] PhantomData, ); -impl AsRef<[u8]> for HashOf { - fn as_ref(&self) -> &[u8] { - Hash::as_ref(&self.0) - } -} - -impl From> for Hash { - fn from(HashOf(hash, _): HashOf) -> Self { - hash +impl fmt::Debug for HashOf { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple(core::any::type_name::()) + .field(&self.0) + .finish() } } @@ -99,18 +102,18 @@ impl Copy for HashOf {} impl PartialEq for HashOf { fn eq(&self, other: &Self) -> bool { - self.0.eq(other) + self.0.eq(&other.0) } } impl Eq for HashOf {} impl PartialOrd for HashOf { - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { self.0.partial_cmp(&other.0) } } impl Ord for HashOf { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { self.0.cmp(&other.0) } } @@ -121,6 +124,18 @@ impl hash::Hash for HashOf { } } +impl AsRef<[u8]> for HashOf { + fn as_ref(&self) -> &[u8] { + Hash::as_ref(&self.0) + } +} + +impl From> for Hash { + fn from(HashOf(hash, _): HashOf) -> Self { + hash + } +} + impl HashOf { /// Unsafe constructor for typed hash pub const fn from_hash(hash: Hash) -> Self { @@ -128,8 +143,6 @@ impl HashOf { } /// Transmutes hash to some specific type - /// SAFETY: - /// Do at your own risk pub const fn transmute(self) -> HashOf { HashOf(self.0, PhantomData) } @@ -137,6 +150,7 @@ impl HashOf { impl HashOf { /// Constructor for typed hash + #[cfg(feature = "std")] pub fn new(value: &T) -> Self { Self(Hash::new(&value.encode()), PhantomData) } @@ -152,13 +166,14 @@ impl IntoSchema for HashOf { mod tests { #![allow(clippy::restriction)] + #[cfg(feature = "std")] use hex_literal::hex; - use ursa::blake2::{ - digest::{Update, VariableOutput}, - VarBlake2b, - }; + + #[cfg(feature = "std")] + use super::*; #[test] + #[cfg(feature = "std")] fn blake2_32b() { let mut hasher = VarBlake2b::new(32).unwrap(); hasher.update(hex!("6920616d2064617461")); diff --git a/crypto/src/lib.rs b/crypto/src/lib.rs index 53ec1f8612e..4b85750895a 100644 --- a/crypto/src/lib.rs +++ b/crypto/src/lib.rs @@ -1,24 +1,29 @@ //! This module contains structures and implementations related to the cryptographic parts of the Iroha. -pub use ursa; +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(not(feature = "std"))] +extern crate alloc; mod hash; pub mod multihash; mod signature; mod varint; -use std::{ - fmt::{self, Debug, Display, Formatter}, - str::FromStr, -}; +#[cfg(not(feature = "std"))] +use alloc::{format, string::String, vec::Vec}; +use core::{fmt, str::FromStr}; -use eyre::{eyre, Error, Result}; +use derive_more::Display; pub use hash::*; use iroha_schema::IntoSchema; use multihash::{DigestFunction as MultihashDigestFunction, Multihash}; use parity_scale_codec::{Decode, Encode}; use serde::{de::Error as SerdeError, Deserialize, Serialize}; pub use signature::*; +#[cfg(feature = "std")] +pub use ursa; +#[cfg(feature = "std")] use ursa::{ keys::{KeyGenOption as UrsaKeyGenOption, PrivateKey as UrsaPrivateKey}, signatures::{ @@ -38,6 +43,14 @@ pub const BLS_NORMAL: &str = "bls_normal"; /// bls small pub const BLS_SMALL: &str = "bls_small"; +/// Error indicating algorithm could not be found +#[derive(Debug, Clone, Copy, Display, IntoSchema)] +#[display(fmt = "Algorithm not supported")] +pub struct NoSuchAlgorithm; + +#[cfg(feature = "std")] +impl std::error::Error for NoSuchAlgorithm {} + /// Algorithm for hashing #[derive(Debug, Clone, Copy)] pub enum Algorithm { @@ -58,20 +71,21 @@ impl Default for Algorithm { } impl FromStr for Algorithm { - type Err = Error; - fn from_str(algorithm: &str) -> Result { + type Err = NoSuchAlgorithm; + + fn from_str(algorithm: &str) -> Result { match algorithm { ED_25519 => Ok(Algorithm::Ed25519), SECP_256_K1 => Ok(Algorithm::Secp256k1), BLS_NORMAL => Ok(Algorithm::BlsNormal), BLS_SMALL => Ok(Algorithm::BlsSmall), - _ => Err(eyre!("The {} algorithm is not supported.")), + _ => Err(Self::Err {}), } } } -impl Display for Algorithm { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { +impl fmt::Display for Algorithm { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Algorithm::Ed25519 => write!(f, "{}", ED_25519), Algorithm::Secp256k1 => write!(f, "{}", SECP_256_K1), @@ -90,21 +104,19 @@ pub enum KeyGenOption { FromPrivateKey(PrivateKey), } +#[cfg(feature = "std")] impl TryFrom for UrsaKeyGenOption { - type Error = Error; + type Error = NoSuchAlgorithm; - fn try_from(key_gen_option: KeyGenOption) -> Result { + fn try_from(key_gen_option: KeyGenOption) -> Result { match key_gen_option { KeyGenOption::UseSeed(seed) => Ok(UrsaKeyGenOption::UseSeed(seed)), KeyGenOption::FromPrivateKey(key) => { if key.digest_function == ED_25519 || key.digest_function == SECP_256_K1 { - Ok(UrsaKeyGenOption::FromSecretKey(UrsaPrivateKey(key.payload))) - } else { - Err(eyre!( - "Ursa does not support {} digest function.", - key.digest_function - )) + return Ok(Self::FromSecretKey(UrsaPrivateKey(key.payload))); } + + Err(Self::Error {}) } } } @@ -132,7 +144,7 @@ impl KeyGenConfiguration { self } - /// with algorithm + /// With algorithm pub fn with_algorithm(mut self, algorithm: Algorithm) -> Self { self.algorithm = algorithm; self @@ -148,12 +160,54 @@ pub struct KeyPair { pub private_key: PrivateKey, } +/// Error when dealing with cryptographic functions +#[cfg(feature = "std")] +#[derive(Debug, Display)] +pub enum Error { + /// Returned when trying to create an algorithm which does not exist + NoSuchAlgorithm, + /// Occurs during deserialization of a private or public key + Parse(String), + /// Returned when an error occurs during the signing process + Signing(String), + /// Returned when an error occurs during key generation + KeyGen(String), + /// Returned when an error occurs during digest generation + DigestGen(String), + /// A General purpose error message that doesn't fit in any category + Other(String), +} + +#[cfg(feature = "std")] +impl From for Error { + fn from(source: ursa::CryptoError) -> Self { + match source { + ursa::CryptoError::NoSuchAlgorithm(_) => Self::NoSuchAlgorithm, + ursa::CryptoError::ParseError(source) => Self::Parse(source), + ursa::CryptoError::SigningError(source) => Self::Signing(source), + ursa::CryptoError::KeyGenError(source) => Self::KeyGen(source), + ursa::CryptoError::DigestGenError(source) => Self::DigestGen(source), + ursa::CryptoError::GeneralError(source) => Self::Other(source), + } + } +} +#[cfg(feature = "std")] +impl From for Error { + fn from(_: NoSuchAlgorithm) -> Self { + Self::NoSuchAlgorithm + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error {} + +#[cfg(feature = "std")] impl KeyPair { /// Generates a pair of Public and Private key with [`Algorithm::default()`] selected as generation algorithm. /// /// # Errors /// Fails if decoding fails - pub fn generate() -> Result { + pub fn generate() -> Result { Self::generate_with_configuration(KeyGenConfiguration::default()) } @@ -161,7 +215,7 @@ impl KeyPair { /// /// # Errors /// Fails if decoding fails - pub fn generate_with_configuration(configuration: KeyGenConfiguration) -> Result { + pub fn generate_with_configuration(configuration: KeyGenConfiguration) -> Result { let key_gen_option: Option = configuration .key_gen_option .map(TryInto::try_into) @@ -171,11 +225,9 @@ impl KeyPair { Algorithm::Secp256k1 => EcdsaSecp256k1Sha256::new().keypair(key_gen_option), Algorithm::BlsNormal => BlsNormal::new().keypair(key_gen_option), Algorithm::BlsSmall => BlsSmall::new().keypair(key_gen_option), - } - // TODO: Create an issue for ursa to impl Error for ursa::CryptoError - //.wrap_err("Failed to generate key pair")?; - .map_err(|e| eyre!("{}", e.to_string()))?; - Ok(KeyPair { + }?; + + Ok(Self { public_key: PublicKey { digest_function: configuration.algorithm.to_string(), payload: public_key.as_ref().to_vec(), @@ -194,6 +246,30 @@ impl From for (PublicKey, PrivateKey) { } } +/// Error which occurs when parsing [`PublicKey`] +#[derive(Debug, Clone, Display)] +pub enum KeyParseError { + /// Decoding hex failed + Decode(hex::FromHexError), + /// Converting bytes to multihash failed + Multihash(multihash::ConvertError), +} + +impl From for KeyParseError { + fn from(source: hex::FromHexError) -> Self { + Self::Decode(source) + } +} + +impl From for KeyParseError { + fn from(source: multihash::ConvertError) -> Self { + Self::Multihash(source) + } +} + +#[cfg(feature = "std")] +impl std::error::Error for KeyParseError {} + /// Public Key used in signatures. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Decode, Encode, IntoSchema)] pub struct PublicKey { @@ -204,12 +280,13 @@ pub struct PublicKey { } impl FromStr for PublicKey { - type Err = Error; + type Err = KeyParseError; - fn from_str(key: &str) -> Result { + fn from_str(key: &str) -> Result { let bytes = hex::decode(key)?; let multihash = Multihash::try_from(bytes)?; - multihash.try_into() + + Ok(multihash.into()) } } @@ -220,8 +297,8 @@ impl Default for PublicKey { } } -impl Debug for PublicKey { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { +impl fmt::Debug for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PublicKey") .field("digest_function", &self.digest_function) .field("payload", &hex::encode_upper(self.payload.as_slice())) @@ -229,9 +306,9 @@ impl Debug for PublicKey { } } -impl Display for PublicKey { +impl fmt::Display for PublicKey { #[allow(clippy::expect_used, clippy::unwrap_in_result)] - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let multihash: &Multihash = &self .clone() .try_into() @@ -243,35 +320,35 @@ impl Display for PublicKey { } } -impl TryFrom for PublicKey { - type Error = Error; - - fn try_from(multihash: Multihash) -> Result { - match multihash.digest_function { - MultihashDigestFunction::Ed25519Pub => Ok(ED_25519), - MultihashDigestFunction::Secp256k1Pub => Ok(SECP_256_K1), - MultihashDigestFunction::Bls12381G1Pub => Ok(BLS_NORMAL), - MultihashDigestFunction::Bls12381G2Pub => Ok(BLS_SMALL), - } - .map(|digest_function| PublicKey { - digest_function: digest_function.to_owned(), +impl From for PublicKey { + fn from(multihash: Multihash) -> Self { + let digest_function = match multihash.digest_function { + MultihashDigestFunction::Ed25519Pub => ED_25519, + MultihashDigestFunction::Secp256k1Pub => SECP_256_K1, + MultihashDigestFunction::Bls12381G1Pub => BLS_NORMAL, + MultihashDigestFunction::Bls12381G2Pub => BLS_SMALL, + }; + + Self { + digest_function: String::from(digest_function), payload: multihash.payload, - }) + } } } impl TryFrom for Multihash { - type Error = Error; + type Error = NoSuchAlgorithm; - fn try_from(public_key: PublicKey) -> Result { - match public_key.digest_function.as_str() { + fn try_from(public_key: PublicKey) -> Result { + let digest_function = match public_key.digest_function.as_str() { ED_25519 => Ok(MultihashDigestFunction::Ed25519Pub), SECP_256_K1 => Ok(MultihashDigestFunction::Secp256k1Pub), BLS_NORMAL => Ok(MultihashDigestFunction::Bls12381G1Pub), BLS_SMALL => Ok(MultihashDigestFunction::Bls12381G2Pub), - _ => Err(eyre!("Digest function not implemented.")), - } - .map(|digest_function| Multihash { + _ => Err(Self::Error {}), + }?; + + Ok(Self { digest_function, payload: public_key.payload, }) @@ -292,7 +369,12 @@ impl<'de> Deserialize<'de> for PublicKey { where D: serde::Deserializer<'de>, { - let public_key_str = >::deserialize(deserializer)?; + #[cfg(not(feature = "std"))] + use alloc::borrow::Cow; + #[cfg(feature = "std")] + use std::borrow::Cow; + + let public_key_str = >::deserialize(deserializer)?; PublicKey::from_str(&public_key_str).map_err(SerdeError::custom) } } @@ -303,26 +385,12 @@ pub struct PrivateKey { /// Digest function pub digest_function: String, /// key payload. WARNING! Do not use `"string".as_bytes()` to obtain the key. - #[serde(deserialize_with = "from_hex", serialize_with = "to_hex")] + #[serde(with = "hex::serde")] pub payload: Vec, } -fn from_hex<'de, D>(deserializer: D) -> Result, D::Error> -where - D: serde::Deserializer<'de>, -{ - hex::decode(String::deserialize(deserializer)?).map_err(SerdeError::custom) -} - -fn to_hex(payload: &[u8], serializer: S) -> Result -where - S: serde::Serializer, -{ - serializer.serialize_str(&hex::encode(payload)) -} - -impl Debug for PrivateKey { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { +impl fmt::Debug for PrivateKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PrivateKey") .field("digest_function", &self.digest_function) .field("payload", &format!("{:X?}", self.payload)) @@ -330,8 +398,8 @@ impl Debug for PrivateKey { } } -impl Display for PrivateKey { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { +impl fmt::Display for PrivateKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", hex::encode(&self.payload)) } } @@ -345,7 +413,8 @@ pub mod prelude { mod tests { #![allow(clippy::restriction)] - use serde::Deserialize; + #[cfg(not(feature = "std"))] + use alloc::borrow::ToOwned as _; use super::*; diff --git a/crypto/src/multihash.rs b/crypto/src/multihash.rs index f15594dfa29..c400fe96ab0 100644 --- a/crypto/src/multihash.rs +++ b/crypto/src/multihash.rs @@ -1,10 +1,11 @@ //! Module with multihash implementation -use std::{fmt::Display, str::FromStr}; +#[cfg(not(feature = "std"))] +use alloc::{vec, vec::Vec}; -use eyre::{eyre, Error, Result}; +use derive_more::Display; -use super::varint::VarUint; +use crate::{varint, NoSuchAlgorithm}; /// ed25519 public string pub const ED_25519_PUB_STR: &str = "ed25519-pub"; @@ -18,15 +19,19 @@ pub const BLS12_381_G2_PUB: &str = "bls12_381-g2-pub"; /// Type of digest function. /// The corresponding byte codes are taken from [official multihash table](https://github.com/multiformats/multicodec/blob/master/table.csv) #[repr(u64)] -#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)] pub enum DigestFunction { /// Ed25519 + #[display(fmt = "{}", "ED_25519_PUB_STR")] Ed25519Pub = 0xed, /// Secp256k1 + #[display(fmt = "{}", "SECP_256_K1_PUB_STR")] Secp256k1Pub = 0xe7, /// Bls12381G1 + #[display(fmt = "{}", "BLS12_381_G1_PUB")] Bls12381G1Pub = 0xea, /// Bls12381G2 + #[display(fmt = "{}", "BLS12_381_G2_PUB")] Bls12381G2Pub = 0xeb, } @@ -36,41 +41,24 @@ impl Default for DigestFunction { } } -impl Display for DigestFunction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match *self { - DigestFunction::Ed25519Pub => write!(f, "{}", ED_25519_PUB_STR), - DigestFunction::Secp256k1Pub => write!(f, "{}", SECP_256_K1_PUB_STR), - DigestFunction::Bls12381G1Pub => write!(f, "{}", BLS12_381_G1_PUB), - DigestFunction::Bls12381G2Pub => write!(f, "{}", BLS12_381_G2_PUB), - } - } -} - -impl FromStr for DigestFunction { - type Err = Error; +impl core::str::FromStr for DigestFunction { + type Err = NoSuchAlgorithm; - fn from_str(s: &str) -> Result { - match s { + fn from_str(source: &str) -> Result { + match source { ED_25519_PUB_STR => Ok(DigestFunction::Ed25519Pub), SECP_256_K1_PUB_STR => Ok(DigestFunction::Secp256k1Pub), BLS12_381_G1_PUB => Ok(DigestFunction::Bls12381G1Pub), BLS12_381_G2_PUB => Ok(DigestFunction::Bls12381G2Pub), - _ => Err(eyre!("The specified digest function is not supported.",)), + _ => Err(Self::Err {}), } } } -impl From<&DigestFunction> for u64 { - fn from(digest_function: &DigestFunction) -> Self { - *digest_function as u64 - } -} - impl TryFrom for DigestFunction { - type Error = Error; + type Error = NoSuchAlgorithm; - fn try_from(variant: u64) -> Result { + fn try_from(variant: u64) -> Result { match variant { variant if variant == DigestFunction::Ed25519Pub as u64 => { Ok(DigestFunction::Ed25519Pub) @@ -84,13 +72,42 @@ impl TryFrom for DigestFunction { variant if variant == DigestFunction::Bls12381G2Pub as u64 => { Ok(DigestFunction::Bls12381G2Pub) } - _ => Err(eyre!("The specified digest function is not supported.",)), + _ => Err(Self::Error {}), + } + } +} + +impl From for u64 { + fn from(digest_function: DigestFunction) -> Self { + digest_function as u64 + } +} + +/// Error which may occur when converting to/from `Multihash` +#[derive(Debug, Clone, Display)] +pub struct ConvertError { + reason: String, +} + +impl ConvertError { + fn new(reason: String) -> Self { + Self { reason } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ConvertError {} + +impl From for ConvertError { + fn from(_: NoSuchAlgorithm) -> Self { + Self { + reason: String::from("Digest function not supported"), } } } /// Multihash -#[derive(Eq, PartialEq, Debug, Default)] +#[derive(Debug, Default, PartialEq, Eq)] pub struct Multihash { /// digest pub digest_function: DigestFunction, @@ -99,46 +116,66 @@ pub struct Multihash { } impl TryFrom> for Multihash { - type Error = Error; - fn try_from(bytes: Vec) -> Result { + type Error = ConvertError; + + fn try_from(bytes: Vec) -> Result { let idx = bytes .iter() .enumerate() .find(|&(_, &byte)| (byte & 0b1000_0000) == 0) - .ok_or_else(|| eyre!("Last byte should be less than 128"))? + .ok_or_else(|| { + Self::Error::new(String::from( + "Failed to find last byte(byte smaller than 128)", + )) + })? .0; + let (digest_function, bytes) = bytes.split_at(idx + 1); let mut bytes = bytes.iter().copied(); - let digest_function: u64 = VarUint::new(&digest_function)?.try_into()?; + let digest_function: u64 = varint::VarUint::new(digest_function) + .map_err(|err| Self::Error::new(err.to_string()))? + .try_into() + .map_err(|err: varint::ConvertError| Self::Error::new(err.to_string()))?; let digest_function = digest_function.try_into()?; let digest_size = bytes .next() - .ok_or_else(|| eyre!("Failed to parse digest size."))?; + .ok_or_else(|| Self::Error::new(String::from("Digest size not found")))?; + let payload: Vec = bytes.collect(); - if payload.len() == digest_size as usize { - Ok(Multihash { - digest_function, - payload, - }) - } else { - Err(eyre!("The digest size is not equal to the actual length.",)) + if payload.len() != digest_size as usize { + return Err(Self::Error::new(String::from( + "Digest size not equal to actual length", + ))); } + + Ok(Self { + digest_function, + payload, + }) } } impl TryFrom<&Multihash> for Vec { - type Error = Error; + type Error = ConvertError; - fn try_from(multihash: &Multihash) -> Result { - let mut bytes = Vec::new(); - let digest_function: u64 = (&multihash.digest_function).into(); - let digest_function: VarUint = digest_function.into(); + fn try_from(multihash: &Multihash) -> Result { + let mut bytes = vec![]; + + let digest_function: u64 = multihash.digest_function.into(); + let digest_function: varint::VarUint = digest_function.into(); let mut digest_function = digest_function.into(); bytes.append(&mut digest_function); - bytes.push(multihash.payload.len().try_into()?); + bytes.push( + multihash + .payload + .len() + .try_into() + .map_err(|_e| ConvertError::new(String::from("Digest size can't fit into u8")))?, + ); bytes.extend_from_slice(&multihash.payload); + Ok(bytes) } } @@ -181,4 +218,9 @@ mod tests { let multihash_decoded: Multihash = bytes.try_into().expect("Failed to decode."); assert_eq!(multihash, multihash_decoded) } + + #[test] + fn digest_function_display() { + assert_eq!(DigestFunction::Ed25519Pub.to_string(), ED_25519_PUB_STR); + } } diff --git a/crypto/src/signature.rs b/crypto/src/signature.rs index 5c6fd265f99..ccf4bc6a4d2 100644 --- a/crypto/src/signature.rs +++ b/crypto/src/signature.rs @@ -1,15 +1,20 @@ -use std::{ - collections::BTreeMap, - error::Error as StdError, - fmt::{self, Debug, Display, Formatter}, - marker::PhantomData, +#[cfg(not(feature = "std"))] +use alloc::{ + boxed::Box, + collections::{btree_map, btree_set}, + format, + string::String, + vec::Vec, }; +use core::{fmt, marker::PhantomData}; +#[cfg(feature = "std")] +use std::collections::{btree_map, btree_set}; use derive_more::{Deref, DerefMut}; -use eyre::{eyre, Context, Result}; use iroha_schema::IntoSchema; use parity_scale_codec::{Decode, Encode}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "std")] use ursa::{ keys::{PrivateKey as UrsaPrivateKey, PublicKey as UrsaPublicKey}, signatures::{ @@ -20,11 +25,14 @@ use ursa::{ }, }; -use super::{Algorithm, KeyPair, PublicKey}; -use crate::HashOf; +use crate::PublicKey; +#[cfg(feature = "std")] +use crate::{Algorithm, Error, HashOf, KeyPair}; /// Represents signature of the data (`Block` or `Transaction` for example). -#[derive(Clone, Encode, Decode, Serialize, Deserialize, PartialOrd, Ord, IntoSchema)] +#[derive( + Clone, PartialEq, Eq, PartialOrd, Ord, Encode, Decode, Serialize, Deserialize, IntoSchema, +)] pub struct Signature { /// Public key that is used for verification. Payload is verified by algorithm /// that corresponds with the public key's digest function. @@ -33,6 +41,7 @@ pub struct Signature { signature: Vec, } +#[cfg(feature = "std")] impl Signature { /// Creates new [`Signature`] by signing payload via [`KeyPair::private_key`]. /// @@ -44,23 +53,18 @@ impl Signature { private_key, }: KeyPair, payload: &[u8], - ) -> Result { - let private_key = UrsaPrivateKey(private_key.payload); + ) -> Result { let algorithm: Algorithm = public_key.digest_function.parse()?; + let private_key = UrsaPrivateKey(private_key.payload); + let signature = match algorithm { Algorithm::Ed25519 => Ed25519Sha512::new().sign(payload, &private_key), Algorithm::Secp256k1 => EcdsaSecp256k1Sha256::new().sign(payload, &private_key), Algorithm::BlsSmall => BlsSmall::new().sign(payload, &private_key), Algorithm::BlsNormal => BlsNormal::new().sign(payload, &private_key), - } - .map_err(|e| { - eyre!( - "Failed to sign payload with public_key {}: {}", - public_key, - e - ) - })?; - Ok(Signature { + }?; + + Ok(Self { public_key, signature, }) @@ -70,10 +74,11 @@ impl Signature { /// /// # Errors /// Fails if decoding digest of key pair fails or if message didn't pass verification - pub fn verify(&self, payload: &[u8]) -> Result<()> { - let public_key = UrsaPublicKey(self.public_key.payload.clone()); + pub fn verify(&self, payload: &[u8]) -> Result<(), Error> { let algorithm: Algorithm = self.public_key.digest_function.parse()?; - let result = match algorithm { + let public_key = UrsaPublicKey(self.public_key.payload.clone()); + + match algorithm { Algorithm::Ed25519 => { Ed25519Sha512::new().verify(payload, &self.signature, &public_key) } @@ -82,25 +87,15 @@ impl Signature { } Algorithm::BlsSmall => BlsSmall::new().verify(payload, &self.signature, &public_key), Algorithm::BlsNormal => BlsNormal::new().verify(payload, &self.signature, &public_key), - }; - match result { - Ok(true) => Ok(()), - _ => Err(eyre!("Signature did not pass verification: {}")), - } - } -} + }?; -impl PartialEq for Signature { - fn eq(&self, other: &Self) -> bool { - self.public_key == other.public_key && self.signature.clone() == other.signature.clone() + Ok(()) } } -impl Eq for Signature {} - -impl Debug for Signature { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("Signature") +impl fmt::Debug for Signature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct(core::any::type_name::()) .field("public_key", &self.public_key) .field("signature", &hex::encode_upper(self.signature.as_slice())) .finish() @@ -108,89 +103,78 @@ impl Debug for Signature { } /// Represents signature of the data (`Block` or `Transaction` for example). +// Lint triggers when expanding #[codec(skip)] +#[allow(clippy::default_trait_access)] #[allow(clippy::unsafe_derive_deserialize)] -#[derive(Debug, Serialize, Deserialize, Deref, DerefMut)] +#[derive(Deref, DerefMut, Decode, Encode, Serialize, Deserialize)] #[serde(transparent)] +// Transmute guard +#[repr(transparent)] pub struct SignatureOf( #[deref] #[deref_mut] Signature, - #[serde(skip)] PhantomData, + #[codec(skip)] PhantomData, ); -impl PartialEq for SignatureOf { - fn eq(&self, other: &Self) -> bool { - self.signature.eq(&other.signature) +impl fmt::Debug for SignatureOf { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple(core::any::type_name::()) + .field(&self.0) + .finish() } } -impl Eq for SignatureOf {} -impl PartialOrd for SignatureOf { - fn partial_cmp(&self, other: &Self) -> Option { - self.signature.partial_cmp(&other.signature) +impl Clone for SignatureOf { + fn clone(&self) -> Self { + Self(self.0.clone(), PhantomData) } } -impl Ord for SignatureOf { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.signature.cmp(&other.signature) + +impl PartialEq for SignatureOf { + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) } } +impl Eq for SignatureOf {} -impl Encode for SignatureOf { - fn size_hint(&self) -> usize { - Signature::size_hint(&self.0) - } - fn encode_to(&self, dest: &mut U) { - self.0.encode_to(dest) - } - fn encode(&self) -> Vec { - self.0.encode() - } - fn using_encoded R>(&self, f: F) -> R { - self.0.using_encoded(f) +impl PartialOrd for SignatureOf { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) } - fn encoded_size(&self) -> usize { - self.0.encoded_size() +} +impl Ord for SignatureOf { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.0.cmp(&other.0) } } -impl Decode for SignatureOf { - fn decode( - input: &mut I, - ) -> Result { - Ok(Self(Signature::decode(input)?, PhantomData)) - } - fn skip(input: &mut I) -> Result<(), parity_scale_codec::Error> { - Signature::skip(input) - } - fn encoded_fixed_size() -> Option { - Signature::encoded_fixed_size() +impl IntoSchema for SignatureOf { + fn schema(metamap: &mut iroha_schema::MetaMap) { + Signature::schema(metamap) } } impl SignatureOf { - /// Verifies signature for this hash + /// Create new [`SignatureOf`] via signing `T` values by [`KeyPair::private_key`] + /// /// # Errors - /// Fails if verification fails - pub fn verify_hash(&self, hash: &HashOf) -> Result<(), SignatureVerificationFail> { - self.0 - .verify(hash.as_ref()) - .map_err(|err| SignatureVerificationFail { - signature: Box::new(self.clone()), - reason: err.to_string(), - }) + /// Fails if decoding digest of key pair fails + #[cfg(feature = "std")] + pub fn from_hash(key_pair: KeyPair, hash: &HashOf) -> Result { + Ok(Self(Signature::new(key_pair, hash.as_ref())?, PhantomData)) } /// Transmutes signature to some specific type - /// SAFETY: - /// Do at your own risk pub fn transmute(self) -> SignatureOf { SignatureOf(self.0, PhantomData) } /// Transmutes signature to some specific type - /// SAFETY: - /// Do at your own risk + /// + /// # Warning: + /// + /// This method uses [`core::mem::transmute`] internally pub fn transmute_ref(&self) -> &SignatureOf { #[allow(unsafe_code, trivial_casts)] unsafe { @@ -198,52 +182,63 @@ impl SignatureOf { } } - /// Creates new [`SignatureOf`] by signing value via [`KeyPair::private_key`]. + /// Verify signature for this hash + /// /// # Errors - /// Fails if decoding digest of key pair fails - pub fn from_hash(key_pair: KeyPair, hash: &HashOf) -> Result { - Ok(Self(Signature::new(key_pair, hash.as_ref())?, PhantomData)) + /// + /// Forwards the 0-th tuple variant verification error + #[cfg(feature = "std")] + pub fn verify_hash(&self, hash: &HashOf) -> Result<(), Error> { + self.0.verify(hash.as_ref()) } } +#[cfg(feature = "std")] impl SignatureOf { - /// Creates new [`SignatureOf`] by signing value via [`KeyPair::private_key`]. + /// Creates new [`SignatureOf`] by signing value via [`KeyPair::private_key`] + /// /// # Errors /// Fails if decoding digest of key pair fails - pub fn new(key_pair: KeyPair, value: &T) -> Result { + pub fn new(key_pair: KeyPair, value: &T) -> Result { Self::from_hash(key_pair, &HashOf::new(value)) } /// Verifies signature for this item + /// /// # Errors /// Fails if verification fails - pub fn verify(&self, value: &T) -> Result<(), SignatureVerificationFail> { + pub fn verify(&self, value: &T) -> Result<(), Error> { self.verify_hash(&HashOf::new(value)) } } -impl Clone for SignatureOf { - fn clone(&self) -> Self { - Self(self.0.clone(), PhantomData) - } -} - -impl IntoSchema for SignatureOf { - fn type_name() -> String { - Signature::type_name() - } - - fn schema(metamap: &mut iroha_schema::MetaMap) { - Signature::schema(metamap) - } -} - -/// Container for multiple signatures. +/// Container for multiple signatures, each corresponding to a different public key. +/// +/// If signature is added which conflicts with a signature already present in the +/// container, it is not defined which of the two will remain in the container. +/// +/// GUARANTEE 1: This container always contains at least 1 signature +/// GUARANTEE 2: Each signature corresponds to a different public key #[allow(clippy::unsafe_derive_deserialize)] -#[derive(Debug, Encode, PartialEq, Eq, Serialize, Deserialize, IntoSchema)] +#[derive(Decode, Encode, Deserialize, Serialize, IntoSchema)] #[serde(transparent)] +// Transmute guard +#[repr(transparent)] pub struct SignaturesOf { - signatures: BTreeMap>, + // This structure is backed by map because only one signature is allowed per public key. + // In the case of Iroha this means that each peer can sign the payload at most once. + // + // TODO: If uniqueness of public key in this collection would be upheld by other means or + // if it were true that `Signature: Borrow` then set could be used instead of map + signatures: btree_map::BTreeMap>, +} + +impl fmt::Debug for SignaturesOf { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct(core::any::type_name::()) + .field("signatures", &self.signatures) + .finish() + } } impl Clone for SignaturesOf { @@ -252,10 +247,16 @@ impl Clone for SignaturesOf { Self { signatures } } } +impl PartialEq for SignaturesOf { + fn eq(&self, other: &Self) -> bool { + self.signatures.eq(&other.signatures) + } +} +impl Eq for SignaturesOf {} impl IntoIterator for SignaturesOf { type Item = SignatureOf; - type IntoIter = std::collections::btree_map::IntoValues; + type IntoIter = btree_map::IntoValues; fn into_iter(self) -> Self::IntoIter { self.signatures.into_values() } @@ -263,156 +264,156 @@ impl IntoIterator for SignaturesOf { impl<'a, T> IntoIterator for &'a SignaturesOf { type Item = &'a SignatureOf; - type IntoIter = std::collections::btree_map::Values<'a, PublicKey, SignatureOf>; + type IntoIter = btree_map::Values<'a, PublicKey, SignatureOf>; fn into_iter(self) -> Self::IntoIter { self.signatures.values() } } -// SAFETY: As this container should always have at least 1 signature -#[allow(clippy::len_without_is_empty)] +impl Extend> for SignaturesOf { + fn extend(&mut self, iter: T) + where + T: IntoIterator>, + { + for signature in iter { + self.insert(signature); + } + } +} + +impl From> for btree_set::BTreeSet> { + fn from(source: SignaturesOf) -> Self { + source.signatures.into_values().collect() + } +} + +impl TryFrom>> for SignaturesOf { + type Error = Error; + + fn try_from(signatures: btree_set::BTreeSet>) -> Result { + if !signatures.is_empty() { + return Ok(Self { + signatures: signatures + .into_iter() + .map(|signature| (signature.public_key.clone(), signature)) + .collect(), + }); + } + + Err(Error::Other(format!( + "{} must contain at least one signature", + core::any::type_name::() + ))) + } +} + +impl FromIterator> for Result, Error> { + fn from_iter>>(iter: T) -> Self { + let signatures: btree_set::BTreeSet<_> = iter.into_iter().collect(); + signatures.try_into() + } +} + impl SignaturesOf { /// Transmutes signature generic type - /// SAFETY: Check complience of hashes of this types + /// + /// # Warning: + /// + /// This method uses [`core::mem::transmute`] internally + #[allow(unsafe_code)] pub fn transmute(self) -> SignaturesOf { - #[allow(unsafe_code)] - let signatures = unsafe { std::mem::transmute(self.signatures) }; + let signatures = unsafe { core::mem::transmute(self.signatures) }; SignaturesOf { signatures } } /// Builds container using single signature pub fn from_signature(sign: SignatureOf) -> Self { let mut me = Self { - signatures: BTreeMap::new(), + signatures: btree_map::BTreeMap::new(), }; - me.add(sign); + me.insert(sign); me } - /// Constructs from iterator. - /// - /// SAFETY: Doesn't check number of signatures (should be >= 1) and signatures themselves - pub fn from_iter_unchecked(iter: impl IntoIterator>) -> Self - where - T: Send + Sync + 'static, - { - let signatures = iter - .into_iter() - .map(|sign| (sign.public_key.clone(), sign)) - .collect::>(); - Self { signatures } - } - - /// Merges 2 signature collections - pub fn merge(&mut self, mut other: Self) { - self.signatures.append(&mut other.signatures) - } - - /// Adds multiple signatures and replaces the duplicates. - pub fn append(&mut self, signatures: &[SignatureOf]) { - for signature in signatures.iter().cloned() { - self.add(signature.clone()); - } - } - /// Adds a signature. If the signature with this key was present, replaces it. - pub fn add(&mut self, signature: SignatureOf) { + pub fn insert(&mut self, signature: SignatureOf) { self.signatures .insert(signature.public_key.clone(), signature); } - /// Whether signatures contain a signature with the specified `public_key` - pub fn contains(&self, public_key: &PublicKey) -> bool { - self.signatures.contains_key(public_key) + /// Returns signatures that have passed verification. + #[cfg(feature = "std")] + pub fn verified_by_hash(&self, hash: HashOf) -> impl Iterator> { + self.signatures + .values() + .filter(move |sign| sign.verify_hash(&hash).is_ok()) } /// Returns signatures that have passed verification. - pub fn verified_by_hash( - &'_ self, - hash: HashOf, - ) -> impl Iterator> + '_ { + #[cfg(feature = "std")] + pub fn into_verified_by_hash(self, hash: HashOf) -> impl Iterator> { self.signatures - .values() + .into_values() .filter(move |sign| sign.verify_hash(&hash).is_ok()) } /// Returns all signatures. - pub fn values(&self) -> Vec> { - self.signatures.values().cloned().collect() + pub fn iter(&self) -> impl Iterator> { + self.into_iter() } /// Number of signatures. pub fn len(&self) -> usize { self.signatures.len() } -} -impl SignaturesOf { - /// Creates new signatures container. - /// # Errors - /// Might fail in signature creation - pub fn new(key_pair: KeyPair, value: &T) -> Result { - SignatureOf::new(key_pair, value).map(Self::from_signature) + /// Number of signatures. + pub fn is_empty(&self) -> bool { + self.signatures.is_empty() } - /// Constructs from iterator and also validates signatures + /// Verify signatures for this hash + /// /// # Errors - /// Might fail in validation of signatures - pub fn from_iter(value: &T, iter: impl IntoIterator>) -> Result - where - T: Send + Sync + 'static, - { - let signatures = iter - .into_iter() - .map(|sign| (sign.public_key.clone(), sign)) - .collect::>(); - if signatures.is_empty() { - return Err(eyre!("Please supply at least one signature")); - } + /// Fails if verificatoin of any signature fails + #[cfg(feature = "std")] + pub fn verify_hash(&self, hash: &HashOf) -> Result<(), SignatureVerificationFail> { + self.signatures.values().try_for_each(|signature| { + signature + .verify_hash(hash) + .map_err(|error| SignatureVerificationFail::new(signature.clone(), error)) + }) + } +} - let hash = HashOf::new(value); - signatures - .values() - .try_for_each(|sign| sign.verify_hash(&hash)) - .wrap_err("Failed to verify signatures")?; - Ok(Self { signatures }) +#[cfg(feature = "std")] +impl SignaturesOf { + /// Create new signatures container + /// + /// # Errors + /// Forwards [`SignatureOf::new`] errors + pub fn new(key_pair: KeyPair, value: &T) -> Result { + SignatureOf::new(key_pair, value).map(Self::from_signature) } /// Verifies all signatures + /// /// # Errors /// Fails if validation of any signature fails pub fn verify(&self, item: &T) -> Result<(), SignatureVerificationFail> { let hash = HashOf::new(item); - self.signatures - .values() - .try_for_each(|sign| sign.verify_hash(&hash)) + self.verify_hash(&hash) } /// Returns signatures that have passed verification. - pub fn verified(&'_ self, value: &T) -> impl Iterator> + '_ { - let payload = HashOf::new(value); - self.verified_by_hash(payload) - } -} - -impl Decode for SignaturesOf { - fn decode( - input: &mut I, - ) -> Result { - Ok(Self { - signatures: BTreeMap::decode(input)?, - }) - } - fn skip(input: &mut I) -> Result<(), parity_scale_codec::Error> { - BTreeMap::>::skip(input) - } - fn encoded_fixed_size() -> Option { - BTreeMap::>::encoded_fixed_size() + pub fn verified(&self, value: &T) -> impl Iterator> { + let hash = HashOf::new(value); + self.verified_by_hash(hash) } } /// Verification failed of some signature due to following reason -#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Decode, Encode, IntoSchema)] +#[derive(Clone, PartialEq, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)] pub struct SignatureVerificationFail { /// Signature which verification has failed pub signature: Box>, @@ -420,8 +421,19 @@ pub struct SignatureVerificationFail { pub reason: String, } -impl Debug for SignatureVerificationFail { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { +impl SignatureVerificationFail { + // `Self` should consume given `Error` + #[allow(clippy::needless_pass_by_value)] + fn new(signature: SignatureOf, error: Error) -> Self { + Self { + signature: Box::new(signature), + reason: error.to_string(), + } + } +} + +impl fmt::Debug for SignatureVerificationFail { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SignatureVerificationFail") .field("signature", &self.signature.0) .field("reason", &self.reason) @@ -429,8 +441,8 @@ impl Debug for SignatureVerificationFail { } } -impl Display for SignatureVerificationFail { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { +impl fmt::Display for SignatureVerificationFail { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "Failed to verify signatures because of signature {}: {}", @@ -439,16 +451,20 @@ impl Display for SignatureVerificationFail { } } -impl StdError for SignatureVerificationFail {} +#[cfg(feature = "std")] +impl std::error::Error for SignatureVerificationFail {} #[cfg(test)] mod tests { #![allow(clippy::restriction)] + #[cfg(feature = "std")] use super::*; + #[cfg(feature = "std")] use crate::KeyGenConfiguration; #[test] + #[cfg(feature = "std")] fn create_signature_ed25519() { let key_pair = KeyPair::generate_with_configuration( KeyGenConfiguration::default().with_algorithm(Algorithm::Ed25519), @@ -462,6 +478,7 @@ mod tests { } #[test] + #[cfg(feature = "std")] fn create_signature_secp256k1() { let key_pair = KeyPair::generate_with_configuration( KeyGenConfiguration::default().with_algorithm(Algorithm::Secp256k1), @@ -475,6 +492,7 @@ mod tests { } #[test] + #[cfg(feature = "std")] fn create_signature_bls_normal() { let key_pair = KeyPair::generate_with_configuration( KeyGenConfiguration::default().with_algorithm(Algorithm::BlsNormal), @@ -488,6 +506,7 @@ mod tests { } #[test] + #[cfg(feature = "std")] fn create_signature_bls_small() { let key_pair = KeyPair::generate_with_configuration( KeyGenConfiguration::default().with_algorithm(Algorithm::BlsSmall), diff --git a/crypto/src/varint.rs b/crypto/src/varint.rs index a69de253ed9..717a50befff 100644 --- a/crypto/src/varint.rs +++ b/crypto/src/varint.rs @@ -1,4 +1,7 @@ -use eyre::{eyre, Error, Result}; +#[cfg(not(feature = "std"))] +use alloc::{format, string::String, vec::Vec}; + +use derive_more::Display; /// Variable length unsigned int. [ref](https://github.com/multiformats/unsigned-varint) #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -7,18 +10,34 @@ pub struct VarUint { payload: Vec, } +/// Error which occurs when converting to/from `VarUint` +#[derive(Debug, Clone, Display)] +pub struct ConvertError { + reason: String, +} + +impl ConvertError { + fn new(reason: String) -> Self { + Self { reason } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for ConvertError {} + macro_rules! try_from_var_uint( { $( $ty:ty ),* } => { $( #[allow(trivial_numeric_casts)] impl TryFrom for $ty { - type Error = Error; - fn try_from(source: VarUint) -> Result { + type Error = ConvertError; + + fn try_from(source: VarUint) -> Result { let VarUint { payload } = source; - if std::mem::size_of::() * 8 < payload.len() * 7 { - return Err(eyre!( - concat!("Number is too large for type ", stringify!(Self)) - )); + if core::mem::size_of::() * 8 < payload.len() * 7 { + return Err(Self::Error::new(String::from( + concat!("Number too large for ", stringify!($ty)) + ))); } let offsets = (0..payload.len()).map(|i| i * 7); let bytes = payload.into_iter().map(|byte| byte & 0b0111_1111); @@ -54,7 +73,7 @@ macro_rules! from_uint( impl From<$ty> for VarUint { fn from(n: $ty) -> Self { let zeros = n.leading_zeros(); - let end = std::mem::size_of::<$ty>() * 8 - zeros as usize; + let end = core::mem::size_of::<$ty>() * 8 - zeros as usize; let mut payload = (0..end) .step_by(7) @@ -72,23 +91,30 @@ macro_rules! from_uint( from_uint!(u8, u16, u32, u64, u128); impl VarUint { - /// Construct [`VarUint`]. - pub fn new(bytes: impl AsRef<[u8]>) -> Result { + /// Construct `VarUint`. + pub fn new(bytes: impl AsRef<[u8]>) -> Result { let idx = bytes .as_ref() .iter() .enumerate() .find(|&(_, &byte)| (byte & 0b1000_0000) == 0) - .ok_or_else(|| eyre!("Last byte should be less than 128"))? + .ok_or_else(|| { + ConvertError::new(String::from( + "Failed to find last byte(byte smaller than 128)", + )) + })? .0; let (payload, empty) = bytes.as_ref().split_at(idx + 1); let payload = payload.to_vec(); if empty.is_empty() { - Ok(Self { payload }) - } else { - Err(eyre!("Last byte shouldn't be followed by anything")) + return Ok(Self { payload }); } + + Err(ConvertError::new(format!( + "{:?}: found these bytes following last byte", + empty + ))) } } @@ -96,6 +122,9 @@ impl VarUint { mod tests { #![allow(clippy::restriction)] + #[cfg(not(feature = "std"))] + use alloc::vec; + use super::*; #[test] diff --git a/crypto_cli/src/main.rs b/crypto_cli/src/main.rs index 94767a35afc..15d42b9bdc8 100644 --- a/crypto_cli/src/main.rs +++ b/crypto_cli/src/main.rs @@ -64,31 +64,34 @@ fn main() -> Result<(), Report> { .parse::() .wrap_err("Failed to parse algorithm.")?; let key_gen_configuration = KeyGenConfiguration::default().with_algorithm(algorithm); - let keypair: KeyPair = seed_option - .map_or_else( - || -> eyre::Result<_> { - private_key_option.map_or_else( - || KeyPair::generate_with_configuration(key_gen_configuration.clone()), - |private_key| { - KeyPair::generate_with_configuration( - key_gen_configuration.clone().use_private_key(PrivateKey { - digest_function: algorithm.to_string(), - payload: hex::decode(private_key) - .wrap_err("Failed to decode private key.")?, - }), - ) - }, - ) - }, - |seed| -> eyre::Result<_> { - KeyPair::generate_with_configuration( - key_gen_configuration - .clone() - .use_seed(seed.as_bytes().into()), - ) - }, - ) - .wrap_err("Failed to generate keypair.")?; + let keypair: KeyPair = seed_option.map_or_else( + || -> eyre::Result<_> { + private_key_option.map_or_else( + || { + KeyPair::generate_with_configuration(key_gen_configuration.clone()) + .wrap_err("failed to generate key pair") + }, + |private_key| { + KeyPair::generate_with_configuration( + key_gen_configuration.clone().use_private_key(PrivateKey { + digest_function: algorithm.to_string(), + payload: hex::decode(private_key) + .wrap_err("Failed to decode private key.")?, + }), + ) + .wrap_err("Failed to generate key pair") + }, + ) + }, + |seed| -> eyre::Result<_> { + KeyPair::generate_with_configuration( + key_gen_configuration + .clone() + .use_seed(seed.as_bytes().into()), + ) + .wrap_err("Failed to generate key pair") + }, + )?; #[allow(clippy::print_stdout)] if matches.is_present("json") { diff --git a/data_model/src/transaction.rs b/data_model/src/transaction.rs index 5ed7d0c6d34..2f43ff8b333 100644 --- a/data_model/src/transaction.rs +++ b/data_model/src/transaction.rs @@ -156,7 +156,6 @@ impl From for VersionedTransaction { VersionedValidTransaction::V1(transaction) => { let signatures = transaction .signatures - .values() .iter() .cloned() .collect::>(); diff --git a/schema/derive/src/lib.rs b/schema/derive/src/lib.rs index d5ecde2579b..a5c04de7682 100644 --- a/schema/derive/src/lib.rs +++ b/schema/derive/src/lib.rs @@ -106,7 +106,7 @@ fn metadata(data: &Data) -> TokenStream2 { }) => { let expr = syn::parse2(quote! {iroha_schema::Metadata::TupleStruct( iroha_schema::UnnamedFieldsMeta { - types: vec![] + types: Vec::new() } )}) .unwrap(); @@ -137,7 +137,11 @@ fn metadata_for_tuplestructs(fields: &FieldsUnnamed) -> (Vec, Expr) { let expr = syn::parse2(quote! { iroha_schema::Metadata::TupleStruct( iroha_schema::UnnamedFieldsMeta { - types: vec![#(#types),*], + types: { + let mut types = Vec::new(); + #( types.push(#types); )* + types + } } ) }) @@ -153,7 +157,11 @@ fn metadata_for_structs(fields: &FieldsNamed) -> (Vec, Expr) { let expr = syn::parse2(quote! { iroha_schema::Metadata::Struct( iroha_schema::NamedFieldsMeta { - declarations: vec![#(#declarations),*], + declarations: { + let mut declarations = Vec::new(); + #( declarations.push(#declarations); )* + declarations + } } ) }) @@ -206,7 +214,11 @@ fn metadata_for_enums(data_enum: &DataEnum) -> (Vec, Expr) { .collect(); let expr = syn::parse2(quote! { iroha_schema::Metadata::Enum(iroha_schema::EnumMeta { - variants: vec![#(#variants),*], + variants: { + let mut variants = Vec::new(); + #( variants.push(#variants); )* + variants + } }) }) .unwrap();