diff --git a/rust/cardano-blockchain-types/Cargo.toml b/rust/cardano-blockchain-types/Cargo.toml index 80d5557ca3..2a5569806c 100644 --- a/rust/cardano-blockchain-types/Cargo.toml +++ b/rust/cardano-blockchain-types/Cargo.toml @@ -20,8 +20,8 @@ workspace = true [dependencies] pallas = { 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 = "r20250128-01" } -catalyst-types = { version = "0.0.1", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "r20250128-01" } +cbork-utils = { version = "0.0.1", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "r20250212-00" } +catalyst-types = { version = "0.0.1", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "r20250212-00" } ouroboros = "0.18.4" tracing = "0.1.41" diff --git a/rust/cardano-blockchain-types/src/hashes.rs b/rust/cardano-blockchain-types/src/hashes.rs index e46c44a2c2..89cd55aefd 100644 --- a/rust/cardano-blockchain-types/src/hashes.rs +++ b/rust/cardano-blockchain-types/src/hashes.rs @@ -9,5 +9,5 @@ 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) + (PubKeyHash, Blake2b224Hash), ); diff --git a/rust/cardano-blockchain-types/src/lib.rs b/rust/cardano-blockchain-types/src/lib.rs index 087362e8c3..6e7bf47bd4 100644 --- a/rust/cardano-blockchain-types/src/lib.rs +++ b/rust/cardano-blockchain-types/src/lib.rs @@ -10,6 +10,7 @@ mod network; mod point; mod slot; mod txn_index; +mod txn_output_offset; mod txn_witness; pub use auxdata::{ @@ -29,4 +30,5 @@ pub use network::Network; pub use point::Point; pub use slot::Slot; pub use txn_index::TxnIndex; +pub use txn_output_offset::TxnOutputOffset; pub use txn_witness::{TxnWitness, VKeyHash}; diff --git a/rust/cardano-blockchain-types/src/multi_era_block_data.rs b/rust/cardano-blockchain-types/src/multi_era_block_data.rs index 6e92cdc01d..935f26c04e 100644 --- a/rust/cardano-blockchain-types/src/multi_era_block_data.rs +++ b/rust/cardano-blockchain-types/src/multi_era_block_data.rs @@ -24,6 +24,7 @@ use crate::{ point::Point, txn_index::TxnIndex, txn_witness::{TxnWitness, VKeyHash}, + Slot, }; /// Self-referencing CBOR encoded data of a multi-era block. @@ -278,11 +279,25 @@ impl MultiEraBlock { self.decode().txs() } + /// Returns an iterator over `(TxnIndex, MultiEraTx)` pair. + pub fn enumerate_txs(&self) -> impl Iterator { + self.txs() + .into_iter() + .enumerate() + .map(|(i, t)| (i.into(), t)) + } + /// Get the auxiliary data of the block. #[must_use] pub fn aux_data(&self) -> &BlockAuxData { &self.inner.aux_data } + + /// Returns a slot of the block. + #[must_use] + pub fn slot(&self) -> Slot { + self.decode().slot().into() + } } impl Display for MultiEraBlock { diff --git a/rust/cardano-blockchain-types/src/slot.rs b/rust/cardano-blockchain-types/src/slot.rs index ee0c69e231..eef0d433f5 100644 --- a/rust/cardano-blockchain-types/src/slot.rs +++ b/rust/cardano-blockchain-types/src/slot.rs @@ -1,18 +1,14 @@ //! Block Slot -use std::{ - cmp::Ordering, - ops::{MulAssign, Sub}, -}; +use std::ops::{MulAssign, Sub}; use catalyst_types::conversion::from_saturating; use num_bigint::{BigInt, Sign}; use serde::Serialize; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default, Serialize)] - /// Slot on the blockchain, typically one slot equals one second. However chain /// parameters can alter how long a slot is. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize)] pub struct Slot(u64); impl Slot { @@ -45,12 +41,6 @@ impl MulAssign for Slot { } } -impl PartialOrd for Slot { - fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) - } -} - impl Sub for Slot { type Output = Slot; diff --git a/rust/cardano-blockchain-types/src/txn_index.rs b/rust/cardano-blockchain-types/src/txn_index.rs index 7b1480de41..825dc560e0 100644 --- a/rust/cardano-blockchain-types/src/txn_index.rs +++ b/rust/cardano-blockchain-types/src/txn_index.rs @@ -3,7 +3,7 @@ use catalyst_types::conversion::from_saturating; /// Transaction index within a block /// See: -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct TxnIndex(u16); impl< diff --git a/rust/cardano-blockchain-types/src/txn_output_offset.rs b/rust/cardano-blockchain-types/src/txn_output_offset.rs new file mode 100644 index 0000000000..efeb7aff3e --- /dev/null +++ b/rust/cardano-blockchain-types/src/txn_output_offset.rs @@ -0,0 +1,87 @@ +//! A transaction output offset inside the transaction. +use catalyst_types::conversion::from_saturating; + +/// A transaction output offset inside the transaction. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct TxnOutputOffset(u16); + +impl< + T: Copy + + TryInto + + std::ops::Sub + + PartialOrd + + num_traits::identities::Zero, + > From for TxnOutputOffset +{ + fn from(value: T) -> Self { + Self(from_saturating(value)) + } +} + +impl From for i16 { + fn from(val: TxnOutputOffset) -> Self { + i16::try_from(val.0).unwrap_or(i16::MAX) + } +} + +impl From for usize { + fn from(value: TxnOutputOffset) -> Self { + value.0.into() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_from_u8_to_txn_index() { + let txn_index: TxnOutputOffset = 100u8.into(); // u8 is a valid type for conversion + assert_eq!(txn_index.0, 100); + } + + #[test] + fn test_from_u16_to_txn_index() { + let txn_index: TxnOutputOffset = 500u16.into(); // u16 is valid and within range for `TxnOutputOffset` + assert_eq!(txn_index.0, 500); + } + + #[test] + fn test_from_i32_to_txn_index() { + let txn_index: TxnOutputOffset = 1234i32.into(); // i32 can be converted into `TxnOutputOffset` + assert_eq!(txn_index.0, 1234); + } + + #[test] + fn test_from_u32_to_txn_index() { + let txn_index: TxnOutputOffset = 500_000u32.into(); // u32 is larger but should be saturated to `u16::MAX` + assert_eq!(txn_index.0, u16::MAX); + } + + #[test] + fn test_from_large_i32_to_txn_index() { + let txn_index: TxnOutputOffset = 70000i32.into(); // i32 too large for u16, should saturate to `u16::MAX` + assert_eq!(txn_index.0, u16::MAX); + } + + #[test] + fn test_txn_index_to_i16_within_range() { + let txn_index = TxnOutputOffset(100); + let result: i16 = txn_index.into(); // Should successfully convert to i16 + assert_eq!(result, 100); + } + + #[test] + fn test_txn_index_to_i16_with_saturation() { + let txn_index = TxnOutputOffset(u16::MAX); // u16::MAX = 65535, which is too large for i16 + let result: i16 = txn_index.into(); // Should saturate to i16::MAX + assert_eq!(result, i16::MAX); + } + + #[test] + fn test_txn_index_to_i16_with_zero() { + let txn_index = TxnOutputOffset(0); // Should be able to convert to i16 without issue + let result: i16 = txn_index.into(); + assert_eq!(result, 0); + } +}