Skip to content
2 changes: 1 addition & 1 deletion rust/cardano-chain-follower/examples/follow_chains.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ async fn follow_for(
|| (chain_update.immutable() != last_immutable)
|| reached_tip
|| follow_all
|| (updates % RUNNING_UPDATE_INTERVAL == 0)
|| updates.is_multiple_of(RUNNING_UPDATE_INTERVAL)
|| (last_fork != chain_update.data.fork())
{
current_era = this_era;
Expand Down
2 changes: 1 addition & 1 deletion rust/cardano-chain-follower/src/mithril_snapshot_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ fn remove_whitespace(s: &str) -> String {

/// Check if a string is an even number of hex digits.
fn is_hex(s: &str) -> bool {
s.chars().count() % 2 == 0 && s.chars().all(|c| c.is_ascii_hexdigit())
s.chars().count().is_multiple_of(2) && s.chars().all(|c| c.is_ascii_hexdigit())
}

#[cfg(test)]
Expand Down
8 changes: 2 additions & 6 deletions rust/catalyst-types/src/catalyst_id/role_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ pub enum RoleIdError {
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, EnumIs)]
#[repr(u8)]
#[non_exhaustive]
#[derive(Default)]
pub enum RoleId {
/// Primary required role use for voting and commenting.
#[default]
Role0 = 0,
/// Delegated representative (dRep) that vote on behalf of delegators.
DelegatedRepresentative = 1,
Expand Down Expand Up @@ -85,12 +87,6 @@ impl RoleId {
}
}

impl Default for RoleId {
fn default() -> Self {
Self::Role0
}
}

impl From<u8> for RoleId {
fn from(value: u8) -> Self {
match value {
Expand Down
4 changes: 2 additions & 2 deletions rust/hermes-ipfs/examples/pubsub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ async fn main() -> anyhow::Result<()> {
let mut event_stream = hermes_a.pubsub_events(option_topic.clone()).await?;
let mut event_stream_b = hermes_b.pubsub_events(option_topic).await?;

let stream = hermes_a.pubsub_subscribe(topic.to_string()).await?;
let stream_b = hermes_b.pubsub_subscribe(topic.to_string()).await?;
let stream = hermes_a.pubsub_subscribe(topic.clone()).await?;
let stream_b = hermes_b.pubsub_subscribe(topic.clone()).await?;

pin_mut!(stream);
pin_mut!(stream_b);
Expand Down
3 changes: 2 additions & 1 deletion rust/signed_doc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ use cbork_utils::{array::Array, decode_context::DecodeCtx, with_cbor_bytes::With
pub use content::Content;
use decode_context::{CompatibilityPolicy, DecodeContext};
pub use metadata::{
ContentEncoding, ContentType, DocLocator, DocType, DocumentRef, DocumentRefs, Metadata, Section,
Chain, ContentEncoding, ContentType, DocLocator, DocType, DocumentRef, DocumentRefs, Metadata,
Section,
};
use minicbor::{decode, encode, Decode, Decoder, Encode};
pub use signature::{CatalystId, Signatures};
Expand Down
142 changes: 142 additions & 0 deletions rust/signed_doc/src/metadata/chain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//! Document Payload Chain.
//!
//! ref: <https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/signed_doc/metadata/#chain-link>

use std::{fmt::Display, hash::Hash};

use cbork_utils::{array::Array, decode_context::DecodeCtx};

use crate::DocumentRef;

/// Reference to the previous Signed Document in a sequence.
#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize)]
pub struct Chain {
/// The consecutive sequence number of the current document
/// in the chain.
/// The very first document in a sequence is numbered `0` and it
/// *MUST ONLY* increment by one for each successive document in
/// the sequence.
///
/// The FINAL sequence number is encoded with the current height
/// sequence value, negated.
///
/// For example the following values for height define a chain
/// that has 5 documents in the sequence 0-4, the final height
/// is negated to indicate the end of the chain:
/// `0, 1, 2, 3, -4`
///
/// No subsequent document can be chained to a sequence that has
/// a final chain height.
height: i32,
/// Reference to a single Signed Document.
///
/// Can be *ONLY* omitted in the very first document in a sequence.
document_ref: Option<DocumentRef>,
}

impl Display for Chain {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
if let Some(document_ref) = &self.document_ref {
write!(f, "height: {}, document_ref: {}", self.height, document_ref)
} else {
write!(f, "height: {}", self.height)
}
}
}

impl minicbor::Encode<()> for Chain {
fn encode<W: minicbor::encode::Write>(
&self,
e: &mut minicbor::Encoder<W>,
_ctx: &mut (),
) -> Result<(), minicbor::encode::Error<W::Error>> {
e.array(if self.document_ref.is_some() { 2 } else { 1 })?;
self.height.encode(e, &mut ())?;
if let Some(doc_ref) = &self.document_ref {
doc_ref.encode(e, &mut ())?;
}
Ok(())
}
}

impl minicbor::Decode<'_, ()> for Chain {
fn decode(
d: &mut minicbor::Decoder<'_>,
_ctx: &mut (),
) -> Result<Self, minicbor::decode::Error> {
const CONTEXT: &str = "Chain decoding";

let arr = Array::decode(d, &mut DecodeCtx::Deterministic)?;

let Some(height_bytes) = arr.first() else {
return Err(minicbor::decode::Error::message(format!(
"{CONTEXT}: expected [height, ? document_ref], found empty array"
)));
};

let height = minicbor::Decoder::new(height_bytes).int()?;
let height = height.try_into().map_err(minicbor::decode::Error::custom)?;

let document_ref = arr
.get(1)
.map(|bytes| {
let mut d = minicbor::Decoder::new(bytes);
DocumentRef::decode(&mut d, &mut ())
})
.transpose()?;

Ok(Self {
height,
document_ref,
})
}
}

#[cfg(test)]
mod tests {
use catalyst_types::uuid::UuidV7;
use minicbor::{Decode, Decoder, Encode, Encoder};

use super::*;
use crate::DocLocator;

#[test]
fn test_chain_encode_decode_without_doc_ref() {
let chain = Chain {
height: 0,
document_ref: None,
};

let mut buf = Vec::new();
let mut enc = Encoder::new(&mut buf);
chain.encode(&mut enc, &mut ()).unwrap();

let mut dec = Decoder::new(&buf);
let decoded = Chain::decode(&mut dec, &mut ()).unwrap();

assert_eq!(decoded, chain);
}

#[test]
fn test_chain_encode_decode_with_doc_ref() {
let id = UuidV7::new();
let ver = UuidV7::new();

let chain = Chain {
height: 3,
document_ref: Some(DocumentRef::new(id, ver, DocLocator::default())),
};

let mut buf = Vec::new();
let mut enc = Encoder::new(&mut buf);
chain.encode(&mut enc, &mut ()).unwrap();

let mut dec = Decoder::new(&buf);
let decoded = Chain::decode(&mut dec, &mut ()).unwrap();

assert_eq!(decoded, chain);
}
}
52 changes: 40 additions & 12 deletions rust/signed_doc/src/metadata/document_refs/doc_locator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
//! A [CBOR Encoded IPLD Content Identifier](https://github.com/ipld/cid-cbor/)
//! or also known as [IPFS CID](https://docs.ipfs.tech/concepts/content-addressing/#what-is-a-cid).

use std::fmt::Display;
use std::{fmt::Display, ops::Deref, str::FromStr};

use cbork_utils::{decode_context::DecodeCtx, map::Map};
use minicbor::{Decode, Decoder, Encode};

use crate::metadata::document_refs::DocRefError;

/// CBOR tag of IPLD content identifiers (CIDs).
const CID_TAG: u64 = 42;

Expand All @@ -20,23 +22,17 @@ const DOC_LOC_MAP_ITEM: u64 = 1;
#[derive(Clone, Debug, Default, PartialEq, Hash, Eq)]
pub struct DocLocator(Vec<u8>);

impl DocLocator {
#[must_use]
/// Length of the document locator.
pub fn len(&self) -> usize {
self.0.len()
}
impl Deref for DocLocator {
type Target = Vec<u8>;

#[must_use]
/// Is the document locator empty.
pub fn is_empty(&self) -> bool {
self.0.is_empty()
fn deref(&self) -> &Self::Target {
&self.0
}
}

impl From<Vec<u8>> for DocLocator {
fn from(value: Vec<u8>) -> Self {
DocLocator(value)
Self(value)
}
}

Expand All @@ -49,6 +45,38 @@ impl Display for DocLocator {
}
}

impl FromStr for DocLocator {
type Err = DocRefError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
s.strip_prefix("0x")
.map(hex::decode)
.ok_or(DocRefError::HexDecode("missing 0x prefix".to_string()))?
.map(Self)
.map_err(|e| DocRefError::HexDecode(e.to_string()))
}
}

impl<'de> serde::Deserialize<'de> for DocLocator {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: serde::Deserializer<'de> {
let s = String::deserialize(deserializer)?;
s.parse::<DocLocator>().map_err(serde::de::Error::custom)
}
}

impl serde::Serialize for DocLocator {
fn serialize<S>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}

// document_locator = { "cid" => cid }
impl Decode<'_, ()> for DocLocator {
fn decode(
Expand Down
3 changes: 2 additions & 1 deletion rust/signed_doc/src/metadata/document_refs/doc_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ use super::doc_locator::DocLocator;
const DOC_REF_ARR_ITEM: u64 = 3;

/// Reference to a Document.
#[derive(Clone, Debug, PartialEq, Hash, Eq)]
#[derive(Clone, Debug, PartialEq, Hash, Eq, serde::Serialize, serde::Deserialize)]
pub struct DocumentRef {
/// Reference to the Document Id
id: UuidV7,
/// Reference to the Document Ver
ver: UuidV7,
/// Document locator
#[serde(rename = "cid", default)]
doc_locator: DocLocator,
}

Expand Down
Loading
Loading