Skip to content

Commit

Permalink
[schema] hyperledger#2114: Sorted collections support in schemas (hyp…
Browse files Browse the repository at this point in the history
…erledger#2115)

Signed-off-by: Marin Veršić <marin.versic101@gmail.com>
  • Loading branch information
mversic committed May 12, 2022
1 parent 9b0b515 commit 3b0487e
Show file tree
Hide file tree
Showing 14 changed files with 219 additions and 178 deletions.
7 changes: 5 additions & 2 deletions crypto/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,12 +183,15 @@ impl<T: Encode> HashOf<T> {
}
}

impl<T> IntoSchema for HashOf<T> {
impl<T: IntoSchema> IntoSchema for HashOf<T> {
fn type_name() -> String {
format!("{}::HashOf<{}>", module_path!(), T::type_name())
}
fn schema(map: &mut MetaMap) {
Hash::schema(map);

map.entry(Self::type_name()).or_insert_with(|| {
Metadata::TupleStruct(UnnamedFieldsMeta {
Metadata::Tuple(UnnamedFieldsMeta {
types: vec![Hash::type_name()],
})
});
Expand Down
47 changes: 35 additions & 12 deletions crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ impl TryFrom<KeyGenOption> for UrsaKeyGenOption {
match key_gen_option {
KeyGenOption::UseSeed(seed) => Ok(UrsaKeyGenOption::UseSeed(seed)),
KeyGenOption::FromPrivateKey(key) => {
if key.digest_function() == Algorithm::Ed25519
|| key.digest_function() == Algorithm::Secp256k1
{
let algorithm = key.digest_function();

if algorithm == Algorithm::Ed25519 || algorithm == Algorithm::Secp256k1 {
return Ok(Self::FromSecretKey(UrsaPrivateKey(key.payload)));
}

Expand Down Expand Up @@ -155,7 +155,7 @@ impl KeyGenConfiguration {
}

/// Pair of Public and Private keys.
#[derive(Debug, Clone, PartialEq, Eq, Getters, Deserialize, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, Getters, Serialize)]
#[getset(get = "pub")]
pub struct KeyPair {
/// Public Key.
Expand Down Expand Up @@ -205,6 +205,11 @@ impl From<NoSuchAlgorithm> for Error {
impl std::error::Error for Error {}

impl KeyPair {
/// Digest function
pub fn digest_function(&self) -> Algorithm {
self.private_key.digest_function()
}

/// Construct `KeyPair`
pub fn new(public_key: PublicKey, private_key: PrivateKey) -> Self {
Self {
Expand All @@ -228,11 +233,13 @@ impl KeyPair {
/// Fails if decoding fails
#[cfg(feature = "std")]
pub fn generate_with_configuration(configuration: KeyGenConfiguration) -> Result<Self, Error> {
let digest_function = configuration.algorithm.to_string();

let key_gen_option: Option<UrsaKeyGenOption> = configuration
.key_gen_option
.map(TryInto::try_into)
.transpose()?;
let (public_key, private_key) = match configuration.algorithm {
let (mut public_key, mut private_key) = match configuration.algorithm {
Algorithm::Ed25519 => Ed25519Sha512.keypair(key_gen_option),
Algorithm::Secp256k1 => EcdsaSecp256k1Sha256::new().keypair(key_gen_option),
Algorithm::BlsNormal => BlsNormal::new().keypair(key_gen_option),
Expand All @@ -241,17 +248,33 @@ impl KeyPair {

Ok(Self {
public_key: PublicKey {
digest_function: configuration.algorithm.to_string(),
payload: public_key.as_ref().to_vec(),
digest_function: digest_function.clone(),
payload: core::mem::take(&mut public_key.0),
},
private_key: PrivateKey {
digest_function: configuration.algorithm.to_string(),
payload: private_key.as_ref().to_vec(),
digest_function,
payload: core::mem::take(&mut private_key.0),
},
})
}
}

impl<'de> Deserialize<'de> for KeyPair {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
struct KeyPair {
public_key: PublicKey,
private_key: PrivateKey,
}

let key_pair = KeyPair::deserialize(deserializer)?;
Ok(Self::new(key_pair.public_key, key_pair.private_key))
}
}

impl From<KeyPair> for (PublicKey, PrivateKey) {
fn from(key_pair: KeyPair) -> Self {
(key_pair.public_key, key_pair.private_key)
Expand Down Expand Up @@ -316,7 +339,7 @@ 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()))
.field("payload", &hex::encode_upper(self.payload().as_slice()))
.finish()
}
}
Expand Down Expand Up @@ -485,14 +508,14 @@ 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))
.field("payload", &format!("{:X?}", self.payload()))
.finish()
}
}

impl fmt::Display for PrivateKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", hex::encode(&self.payload))
write!(f, "{}", hex::encode(self.payload()))
}
}

Expand Down
10 changes: 8 additions & 2 deletions crypto/src/merkle.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Merkle tree implementation.

#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, vec, vec::Vec};
use alloc::{boxed::Box, format, string::String, vec, vec::Vec};
#[cfg(feature = "std")]
use std::collections::VecDeque;

Expand All @@ -18,10 +18,16 @@ pub struct MerkleTree<T> {
}

impl<T: IntoSchema> IntoSchema for MerkleTree<T> {
fn type_name() -> String {
format!("{}::MerkleTree<{}>", module_path!(), T::type_name())
}
fn schema(map: &mut MetaMap) {
map.entry(Self::type_name()).or_insert_with(|| {
// BFS ordered list of leaf nodes
Metadata::Vec(HashOf::<T>::type_name())
Metadata::Vec(VecMeta {
ty: HashOf::<T>::type_name(),
sorted: true,
})
});
if !map.contains_key(&HashOf::<T>::type_name()) {
HashOf::<T>::schema(map);
Expand Down
48 changes: 23 additions & 25 deletions crypto/src/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ pub struct Signature {
/// that corresponds with the public key's digest function.
public_key: PublicKey,
/// Actual signature payload is placed here.
#[getset(skip)]
payload: Payload,
}

Expand All @@ -64,13 +63,9 @@ impl Signature {
/// # Errors
/// Fails if signing fails
#[cfg(feature = "std")]
fn new(
KeyPair {
public_key,
private_key,
}: KeyPair,
payload: &[u8],
) -> Result<Self, Error> {
fn new(key_pair: KeyPair, payload: &[u8]) -> Result<Self, Error> {
let (public_key, private_key) = key_pair.into();

let algorithm: Algorithm = public_key.digest_function();
let private_key = UrsaPrivateKey(private_key.payload);

Expand All @@ -91,7 +86,7 @@ impl Signature {
/// since it is not possible to validate the correctness of the conversion.
/// Prefer creating new signatures with [`SignatureOf::new`] whenever possible
#[inline]
#[allow(dead_code)]
#[cfg_attr(not(feature = "std"), allow(dead_code))]
fn typed<T>(self) -> SignatureOf<T> {
SignatureOf(self, PhantomData)
}
Expand All @@ -103,15 +98,15 @@ impl Signature {
#[cfg(feature = "std")]
pub fn verify(&self, payload: &[u8]) -> Result<(), Error> {
let algorithm: Algorithm = self.public_key.digest_function();
let public_key = UrsaPublicKey(self.public_key.payload.clone());
let public_key = UrsaPublicKey(self.public_key.payload().clone());

match algorithm {
Algorithm::Ed25519 => Ed25519Sha512::new().verify(payload, &self.payload, &public_key),
Algorithm::Ed25519 => Ed25519Sha512::new().verify(payload, self.payload(), &public_key),
Algorithm::Secp256k1 => {
EcdsaSecp256k1Sha256::new().verify(payload, &self.payload, &public_key)
EcdsaSecp256k1Sha256::new().verify(payload, self.payload(), &public_key)
}
Algorithm::BlsSmall => BlsSmall::new().verify(payload, &self.payload, &public_key),
Algorithm::BlsNormal => BlsNormal::new().verify(payload, &self.payload, &public_key),
Algorithm::BlsSmall => BlsSmall::new().verify(payload, self.payload(), &public_key),
Algorithm::BlsNormal => BlsNormal::new().verify(payload, self.payload(), &public_key),
}?;

Ok(())
Expand All @@ -122,7 +117,7 @@ impl fmt::Debug for Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(core::any::type_name::<Self>())
.field("public_key", &self.public_key)
.field("signature", &hex::encode_upper(self.payload.as_slice()))
.field("signature", &hex::encode_upper(self.payload().as_slice()))
.finish()
}
}
Expand Down Expand Up @@ -191,12 +186,15 @@ impl<T> Ord for SignatureOf<T> {
}
}

impl<T> IntoSchema for SignatureOf<T> {
impl<T: IntoSchema> IntoSchema for SignatureOf<T> {
fn type_name() -> String {
format!("{}::SignatureOf<{}>", module_path!(), T::type_name())
}
fn schema(map: &mut MetaMap) {
Signature::schema(map);

map.entry(Self::type_name()).or_insert_with(|| {
Metadata::TupleStruct(UnnamedFieldsMeta {
Metadata::Tuple(UnnamedFieldsMeta {
types: vec![Signature::type_name()],
})
});
Expand Down Expand Up @@ -273,7 +271,6 @@ impl<T: Encode> SignatureOf<T> {
///
/// 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(Encode, Serialize, IntoSchema)]
#[serde(transparent)]
// Transmute guard
Expand Down Expand Up @@ -381,7 +378,7 @@ impl<T> TryFrom<btree_set::BTreeSet<SignatureOf<T>>> for SignaturesOf<T> {
return Ok(Self {
signatures: signatures
.into_iter()
.map(|signature| (signature.public_key.clone(), signature))
.map(|signature| (signature.public_key().clone(), signature))
.collect(),
});
}
Expand Down Expand Up @@ -417,7 +414,7 @@ impl<T> SignaturesOf<T> {
/// Adds a signature. If the signature with this key was present, replaces it.
pub fn insert(&mut self, signature: SignatureOf<T>) {
self.signatures
.insert(signature.public_key.clone(), signature);
.insert(signature.public_key().clone(), signature);
}

/// Returns signatures that have passed verification.
Expand Down Expand Up @@ -525,7 +522,8 @@ impl<T> fmt::Display for SignatureVerificationFail<T> {
write!(
f,
"Failed to verify signatures because of signature {}: {}",
self.signature.public_key, self.reason,
self.signature.public_key(),
self.reason,
)
}
}
Expand All @@ -552,7 +550,7 @@ mod tests {
let message = b"Test message to sign.";
let signature =
Signature::new(key_pair.clone(), message).expect("Failed to create signature.");
assert_eq!(signature.public_key, key_pair.public_key);
assert_eq!(signature.public_key(), key_pair.public_key());
assert!(signature.verify(message).is_ok())
}

Expand All @@ -566,7 +564,7 @@ mod tests {
let message = b"Test message to sign.";
let signature =
Signature::new(key_pair.clone(), message).expect("Failed to create signature.");
assert_eq!(signature.public_key, key_pair.public_key);
assert_eq!(signature.public_key(), key_pair.public_key());
assert!(signature.verify(message).is_ok())
}

Expand All @@ -580,7 +578,7 @@ mod tests {
let message = b"Test message to sign.";
let signature =
Signature::new(key_pair.clone(), message).expect("Failed to create signature.");
assert_eq!(signature.public_key, key_pair.public_key);
assert_eq!(signature.public_key(), key_pair.public_key());
assert!(signature.verify(message).is_ok())
}

Expand All @@ -594,7 +592,7 @@ mod tests {
let message = b"Test message to sign.";
let signature =
Signature::new(key_pair.clone(), message).expect("Failed to create signature.");
assert_eq!(signature.public_key, key_pair.public_key);
assert_eq!(signature.public_key(), key_pair.public_key());
assert!(signature.verify(message).is_ok())
}

Expand Down
5 changes: 2 additions & 3 deletions data_model/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,13 @@ roles = []

# Internal use only
mutable_api = []
cross_crate_testing = []

[dependencies]
iroha_data_primitives = { path = "primitives", version = "=2.0.0-pre-rc.3", default-features = false }
iroha_crypto = { path = "../crypto", version = "=2.0.0-pre-rc.3", default-features = false }
iroha_macro = { path = "../macro", version = "=2.0.0-pre-rc.3", default-features = false }
iroha_schema = { path = "../schema", version = "=2.0.0-pre-rc.3", default-features = false }
iroha_version = { path = "../version", version = "=2.0.0-pre-rc.3", default-features = false, features = ["derive", "json", "scale"] }
iroha_data_primitives = { path = "primitives", version = "=2.0.0-pre-rc.3", default-features = false }
iroha_schema = { path = "../schema", version = "=2.0.0-pre-rc.3" }

parity-scale-codec = { version = "2.3.1", default-features = false, features = ["derive"] }
derive_more = { version = "0.99.16", default-features = false, features = ["display"] }
Expand Down
5 changes: 4 additions & 1 deletion data_model/src/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ impl<V: TryFrom<Value>> EvaluatesTo<V> {
}
}

impl<V: TryFrom<Value>> IntoSchema for EvaluatesTo<V> {
impl<V: IntoSchema + TryFrom<Value>> IntoSchema for EvaluatesTo<V> {
fn type_name() -> String {
format!("{}::EvaluatesTo<{}>", module_path!(), V::type_name())
}
fn schema(map: &mut MetaMap) {
ExpressionBox::schema(map);

Expand Down
5 changes: 2 additions & 3 deletions data_model/src/role.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
//! Structures, traits and impls related to `Role`s.

#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, collections::btree_set, string::String};
use core::fmt;
use alloc::{collections::btree_set, format, string::String, vec::Vec};
use core::{fmt, str::FromStr};
#[cfg(feature = "std")]
use std::collections::btree_set;
use std::str::FromStr;

use getset::Getters;
use iroha_schema::IntoSchema;
Expand Down
Loading

0 comments on commit 3b0487e

Please sign in to comment.