Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6eb47b4
Add the purpose getter function to Cip509
stanislav-tkach Jan 27, 2025
b477fd5
Implement SerializeValue and DeserializeValue for UUIDs
stanislav-tkach Jan 27, 2025
9383106
Merge branch 'main' into cip509-purpose-getter
stanislav-tkach Jan 27, 2025
4fa68e7
Export Cip509RbacMetadata type
stanislav-tkach Jan 27, 2025
5fb0602
Merge remote-tracking branch 'origin/cip509-purpose-getter' into cip5…
stanislav-tkach Jan 27, 2025
35b42e5
Implement SerializeValue and DeserializeValue for Blake2bHash
stanislav-tkach Jan 27, 2025
26d8a74
Fix Clippy
stanislav-tkach Jan 27, 2025
84a3e02
Merge branch 'main' into cip509-purpose-getter
stanislav-tkach Jan 27, 2025
328733c
Add TransactionHash and PubKeyHash types
stanislav-tkach Jan 27, 2025
33ba72a
Merge remote-tracking branch 'origin/cip509-purpose-getter' into cip5…
stanislav-tkach Jan 27, 2025
4f7cae2
Merge branch 'main' into cip509-purpose-getter
stanislav-tkach Jan 27, 2025
6c26210
Fix spellcheck
stanislav-tkach Jan 27, 2025
d74e1bb
Merge remote-tracking branch 'origin/cip509-purpose-getter' into cip5…
stanislav-tkach Jan 27, 2025
d0f4924
Fix spellcheck 2
stanislav-tkach Jan 27, 2025
344f3dc
Remove unused dependencies
stanislav-tkach Jan 27, 2025
5ed07ab
Document the macro
stanislav-tkach Jan 27, 2025
b697f71
Use 'meta' instead of 'tt' for docs
stanislav-tkach Jan 27, 2025
eab11ac
Remove cspell exception
stanislav-tkach Jan 28, 2025
c853ce7
Remove -scylla dependency
stanislav-tkach Jan 28, 2025
20c169d
Merge branch 'main' into cip509-purpose-getter
stanislav-tkach Jan 28, 2025
3629032
Move define_hashes to catalyst-types
stanislav-tkach Jan 28, 2025
b6bed0e
Remove import
stanislav-tkach Jan 28, 2025
0b832b3
cleanup
stanislav-tkach Jan 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions rust/cardano-blockchain-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ workspace = true

[dependencies]
pallas = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" }
pallas-crypto = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" }
# pallas-hardano = { version = "0.30.1", git = "https://github.com/input-output-hk/catalyst-pallas.git", rev = "9b5183c8b90b90fe2cc319d986e933e9518957b3" }
cbork-utils = { version = "0.0.1", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "r20250127-00" }
catalyst-types = { version = "0.0.1", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "r20250127-00" }
Expand Down
142 changes: 142 additions & 0 deletions rust/cardano-blockchain-types/src/hashes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//! A bunch of specific hashes.
// cspell: words attrss

use std::str::FromStr;

use catalyst_types::hashes::{Blake2b224Hash, Blake2b256Hash, Blake2bHashError};
use pallas_crypto::hash::Hash;

/// Defines a new type wrapper for the given hash type.
macro_rules! define_hashes {
($($(#[$docs:meta])* ($name:ident, $inner:ty)),+) => {
$(
$(#[$docs])*
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct $name($inner);

impl $name {
/// Creates a new instance from the given bytes by hashing them.
#[must_use]
pub fn new(input_bytes: &[u8]) -> Self {
Self(<$inner>::new(input_bytes))
}
}

impl From<$name> for Vec<u8> {
fn from(value: $name) -> Self {
value.0.into()
}
}

impl From<$inner> for $name {
fn from(value: $inner) -> Self {
Self(value)
}
}

impl TryFrom<&[u8]> for $name {
type Error = Blake2bHashError;

fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
Ok(Self(<$inner>::try_from(value)?))
}
}

impl TryFrom<Vec<u8>> for $name {
type Error = Blake2bHashError;

fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
value.as_slice().try_into()
}
}

impl FromStr for $name {
type Err = Blake2bHashError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let hash: $inner = s.parse().map_err(Blake2bHashError::from)?;
Ok(Self(hash))
}
}

impl<C> minicbor::Encode<C> for $name {
fn encode<W: minicbor::encode::Write>(
&self, e: &mut minicbor::Encoder<W>, ctx: &mut C,
) -> Result<(), minicbor::encode::Error<W::Error>> {
self.0.encode(e, ctx)
}
}

impl<'a, C> minicbor::Decode<'a, C> for $name {
fn decode(
d: &mut minicbor::Decoder<'a>, ctx: &mut C,
) -> Result<Self, minicbor::decode::Error> {
let hash = <$inner>::decode(d, ctx)?;
Ok(Self(hash))
}
}

// TODO: Uncomment when the changes to Blake2bHash are merged.
// impl SerializeValue for $name {
// fn serialize<'b>(
// &self, typ: &ColumnType, writer: CellWriter<'b>,
// ) -> Result<WrittenCellProof<'b>, SerializationError> {
// self.0.serialize(typ, writer)
// }
// }
//
// impl<'frame, 'metadata> DeserializeValue<'frame, 'metadata> for $name
// {
// fn type_check(typ: &ColumnType) -> Result<(), TypeCheckError> {
// <$inner>::type_check(typ)
// }
//
// fn deserialize(
// typ: &'metadata ColumnType<'metadata>, v: Option<FrameSlice<'frame>>,
// ) -> Result<Self, DeserializationError> {
// let hash = <$inner>::deserialize(typ, v)?;
// Ok(Self(hash))
// }
// }
)+
};
}

define_hashes!(
/// A transaction hash - Blake2b-256 hash of a transaction.
(TransactionHash, Blake2b256Hash),
/// A public key hash - raw Blake2b-224 hash of an Ed25519 public key (has no discriminator, just the hash).
(PubKeyHash, Blake2b224Hash)
);

impl From<Hash<32>> for TransactionHash {
fn from(hash: Hash<32>) -> Self {
Self(Blake2b256Hash::from(hash))
}
}

impl From<Hash<28>> for PubKeyHash {
fn from(hash: Hash<28>) -> Self {
Self(Blake2b224Hash::from(hash))
}
}

#[cfg(test)]
mod tests {
use super::*;

// There is little reason to check the conversion itself, it is mostly a demonstration
// that the methods defined by the macro are working.
#[test]
fn roundtrip() {
let hash = TransactionHash::new(&[]);

let v = Vec::from(hash);
let from_slice = TransactionHash::try_from(v.as_slice()).unwrap();
assert_eq!(hash, from_slice);

let from_vec = TransactionHash::try_from(v).unwrap();
assert_eq!(hash, from_vec);
}
}
2 changes: 2 additions & 0 deletions rust/cardano-blockchain-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
mod auxdata;
mod cip134_uri;
mod fork;
mod hashes;
mod metadata;
mod multi_era_block_data;
mod network;
Expand All @@ -21,6 +22,7 @@ pub use auxdata::{
};
pub use cip134_uri::Cip0134Uri;
pub use fork::Fork;
pub use hashes::{PubKeyHash, TransactionHash};
pub use metadata::cip36::{voting_pk::VotingPubKey, Cip36};
pub use multi_era_block_data::MultiEraBlock;
pub use network::Network;
Expand Down
1 change: 1 addition & 0 deletions rust/catalyst-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ chrono = "0.4.39"
fmmap = { version = "0.3.3", features = ["sync", "tokio-async"] }
once_cell = "1.20.2"
tracing = "0.1.41"
scylla = "0.15.1"

[dev-dependencies]
ed25519-dalek = { version = "2.1.1", features = ["rand_core"] }
Expand Down
27 changes: 27 additions & 0 deletions rust/catalyst-types/src/hashes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ use std::{fmt, str::FromStr};
use blake2b_simd::Params;
use displaydoc::Display;
use pallas_crypto::hash::Hash;
use scylla::_macro_internal::{
CellWriter, ColumnType, DeserializationError, DeserializeValue, FrameSlice, SerializationError,
SerializeValue, TypeCheckError, WrittenCellProof,
};
use thiserror::Error;

/// Number of bytes in a blake2b 224 hash.
Expand Down Expand Up @@ -165,6 +169,29 @@ impl<'a, C, const BYTES: usize> minicbor::Decode<'a, C> for Blake2bHash<BYTES> {
}
}

impl<const BYTES: usize> SerializeValue for Blake2bHash<BYTES> {
fn serialize<'b>(
&self, typ: &ColumnType, writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
self.0.as_ref().serialize(typ, writer)
}
}

impl<'frame, 'metadata, const BYTES: usize> DeserializeValue<'frame, 'metadata>
for Blake2bHash<BYTES>
{
fn type_check(typ: &ColumnType) -> Result<(), TypeCheckError> {
<Vec<u8>>::type_check(typ)
}

fn deserialize(
typ: &'metadata ColumnType<'metadata>, v: Option<FrameSlice<'frame>>,
) -> Result<Self, DeserializationError> {
let bytes = <Vec<u8>>::deserialize(typ, v)?;
Self::try_from(bytes).map_err(DeserializationError::new)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
35 changes: 34 additions & 1 deletion rust/catalyst-types/src/uuid/uuid_v4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
use std::fmt::{Display, Formatter};

use minicbor::{Decode, Decoder, Encode};
use scylla::{
_macro_internal::{
CellWriter, ColumnType, DeserializationError, FrameSlice, SerializationError,
SerializeValue, TypeCheckError, WrittenCellProof,
},
deserialize::DeserializeValue,
};
use serde::Deserialize;
use uuid::Uuid;

use super::{decode_cbor_uuid, encode_cbor_uuid, CborContext, UuidError, INVALID_UUID};
Expand Down Expand Up @@ -97,7 +105,7 @@ impl From<UuidV4> for Uuid {
impl<'de> serde::Deserialize<'de> for UuidV4 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: serde::Deserializer<'de> {
let uuid = Uuid::deserialize(deserializer)?;
let uuid = <Uuid as Deserialize>::deserialize(deserializer)?;
if is_valid(&uuid) {
Ok(Self(uuid))
} else {
Expand All @@ -106,6 +114,31 @@ impl<'de> serde::Deserialize<'de> for UuidV4 {
}
}

impl SerializeValue for UuidV4 {
fn serialize<'b>(
&self, typ: &ColumnType, writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
self.0.serialize(typ, writer)
}
}

impl<'frame, 'metadata> DeserializeValue<'frame, 'metadata> for UuidV4 {
fn type_check(typ: &ColumnType) -> Result<(), TypeCheckError> {
Uuid::type_check(typ)
}

fn deserialize(
typ: &'metadata ColumnType<'metadata>, v: Option<FrameSlice<'frame>>,
) -> Result<Self, DeserializationError> {
let uuid = <Uuid as DeserializeValue>::deserialize(typ, v)?;
if is_valid(&uuid) {
Ok(Self(uuid))
} else {
Err(DeserializationError::new(UuidError::InvalidUuidV4(uuid)))
}
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
47 changes: 39 additions & 8 deletions rust/catalyst-types/src/uuid/uuid_v7.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@
use std::fmt::{Display, Formatter};

use minicbor::{Decode, Decoder, Encode};
use scylla::_macro_internal::{
CellWriter, ColumnType, DeserializationError, DeserializeValue, FrameSlice, SerializationError,
SerializeValue, TypeCheckError, WrittenCellProof,
};
use serde::Deserialize;
use uuid::Uuid;

use super::{decode_cbor_uuid, encode_cbor_uuid, CborContext, UuidError, INVALID_UUID};

/// Type representing a `UUIDv7`.
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, serde::Serialize)]
pub struct UuidV7(uuid::Uuid);
pub struct UuidV7(Uuid);

impl UuidV7 {
/// Version for `UUIDv7`.
Expand All @@ -17,7 +23,7 @@ impl UuidV7 {
#[must_use]
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self(uuid::Uuid::now_v7())
Self(Uuid::now_v7())
}

/// Generates a zeroed out `UUIDv7` that can never be valid.
Expand All @@ -34,13 +40,13 @@ impl UuidV7 {

/// Returns the `uuid::Uuid` type.
#[must_use]
pub fn uuid(&self) -> uuid::Uuid {
pub fn uuid(&self) -> Uuid {
self.0
}
}

/// Check if this is a valid `UUIDv7`.
fn is_valid(uuid: &uuid::Uuid) -> bool {
fn is_valid(uuid: &Uuid) -> bool {
uuid != &INVALID_UUID && uuid.get_version_num() == UuidV7::UUID_VERSION_NUMBER
}

Expand Down Expand Up @@ -72,10 +78,10 @@ impl Encode<CborContext> for UuidV7 {
}

/// Returns a `UUIDv7` from `uuid::Uuid`.
impl TryFrom<uuid::Uuid> for UuidV7 {
impl TryFrom<Uuid> for UuidV7 {
type Error = UuidError;

fn try_from(uuid: uuid::Uuid) -> Result<Self, Self::Error> {
fn try_from(uuid: Uuid) -> Result<Self, Self::Error> {
if is_valid(&uuid) {
Ok(Self(uuid))
} else {
Expand All @@ -87,7 +93,7 @@ impl TryFrom<uuid::Uuid> for UuidV7 {
/// Returns a `uuid::Uuid` from `UUIDv7`.
///
/// NOTE: This does not guarantee that the `UUID` is valid.
impl From<UuidV7> for uuid::Uuid {
impl From<UuidV7> for Uuid {
fn from(value: UuidV7) -> Self {
value.0
}
Expand All @@ -96,7 +102,7 @@ impl From<UuidV7> for uuid::Uuid {
impl<'de> serde::Deserialize<'de> for UuidV7 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: serde::Deserializer<'de> {
let uuid = uuid::Uuid::deserialize(deserializer)?;
let uuid = <Uuid as Deserialize>::deserialize(deserializer)?;
if is_valid(&uuid) {
Ok(Self(uuid))
} else {
Expand All @@ -105,6 +111,31 @@ impl<'de> serde::Deserialize<'de> for UuidV7 {
}
}

impl SerializeValue for UuidV7 {
fn serialize<'b>(
&self, typ: &ColumnType, writer: CellWriter<'b>,
) -> Result<WrittenCellProof<'b>, SerializationError> {
self.0.serialize(typ, writer)
}
}

impl<'frame, 'metadata> DeserializeValue<'frame, 'metadata> for UuidV7 {
fn type_check(typ: &ColumnType) -> Result<(), TypeCheckError> {
Uuid::type_check(typ)
}

fn deserialize(
typ: &'metadata ColumnType<'metadata>, v: Option<FrameSlice<'frame>>,
) -> Result<Self, DeserializationError> {
let uuid = <Uuid as DeserializeValue>::deserialize(typ, v)?;
if is_valid(&uuid) {
Ok(Self(uuid))
} else {
Err(DeserializationError::new(UuidError::InvalidUuidV4(uuid)))
}
}
}

#[cfg(test)]
mod tests {
use uuid::Uuid;
Expand Down
6 changes: 6 additions & 0 deletions rust/rbac-registration/src/cardano/cip509/cip509.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,12 @@ impl Cip509 {
self.metadata.as_ref().and_then(|m| m.role_data.get(&role))
}

/// Returns a purpose of this registration.
#[must_use]
pub fn purpose(&self) -> Option<UuidV4> {
self.purpose
}

/// Returns a hash of the previous transaction.
#[must_use]
pub fn previous_transaction(&self) -> Option<Blake2b256Hash> {
Expand Down
Loading
Loading