Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
290 changes: 287 additions & 3 deletions Cargo.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ object RuntimeManager {
// val emptyStateHashFixed: StateHash =
// "575c95f165bc2f27c0ef7e90ada4017b316a349f449d44a035f465b5ae8f8508".unsafeHexToByteString
val emptyStateHashFixed: StateHash =
"cb75e7f94e8eac21f95c524a07590f2583fbdaba6fb59291cf52fa16a14c784d".unsafeHexToByteString
"e5ed6104e936efdc33cbca073544a163b052e9100bcae7135abd7d42a35293d4".unsafeHexToByteString

def apply[F[_]](implicit F: RuntimeManager[F]): F.type = F

Expand Down
2 changes: 1 addition & 1 deletion casper/src/rust/util/rholang/runtime_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ impl RuntimeManager {
* the time. For some situations, we can just use the value directly for better performance.
*/
pub fn empty_state_hash_fixed() -> StateHash {
hex::decode("cb75e7f94e8eac21f95c524a07590f2583fbdaba6fb59291cf52fa16a14c784d")
hex::decode("e5ed6104e936efdc33cbca073544a163b052e9100bcae7135abd7d42a35293d4")
.unwrap()
.into()
}
Expand Down
2 changes: 1 addition & 1 deletion casper/tests/util/rholang/runtime_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ async fn state_hash_after_fixed_rholang_term_execution_should_be_hash_fixed_with

let checkpoint = runtime.create_checkpoint();
let expected_hash = Blake2b256Hash::from_hex(
"18e91cdc71e51e08a1a0f3f8aebf7d58b9768e05b7539da02cc953fc9d548fc4",
"9b587b8158fe616e8f9eb9e84a187d74ac1a90fbafea3c68ae37064cbbd93026",
);

assert_eq!(expected_hash, checkpoint.root);
Expand Down
51 changes: 38 additions & 13 deletions models/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,18 @@ fn main() {
.build_client(true)
.build_server(true)
.btree_map(".")
// Apply to messages only (not enums or oneofs - they are handled separately)
.message_attribute(
".rhoapi",
"#[derive(serde::Serialize, serde::Deserialize, utoipa::ToSchema)]",
)
.message_attribute(".rhoapi", "#[derive(Eq, Ord, PartialOrd)]")
.message_attribute(".rhoapi", "#[repr(C)]")
// Apply serde/utoipa to enums (but NOT Eq/Ord/PartialOrd - prost already derives them)
.enum_attribute(
".rhoapi",
"#[derive(serde::Serialize, serde::Deserialize, utoipa::ToSchema)]",
)
.enum_attribute(".rhoapi", "#[derive(Eq, Ord, PartialOrd)]")
.enum_attribute(".rhoapi", "#[repr(C)]")
.bytes(".casper")
.bytes(".routing")
// needed for grpc services from deploy_grpc_service_v1.rs to avoid upper camel case warnings
Expand All @@ -64,26 +64,51 @@ fn main() {
)
.expect("Failed to compile proto files");

// Remove PartialEq from specific generated structs from rhoapi.rs
// Post-process generated rhoapi.rs:
// 1. Remove PartialEq from Messages and Oneofs (we provide manual impls in lib.rs)
// 2. Remove Hash from Oneofs that have manual Hash impls
// 3. Add Eq, Ord, PartialOrd to Oneofs (so messages containing them can derive these traits)
let out_dir = std::env::var("OUT_DIR").unwrap();
let file_path = format!("{}/rhoapi.rs", out_dir);
let content = fs::read_to_string(&file_path).expect("Unable to read file");

let modified_content = content
.lines()
.map(|line| {
if line.contains("#[derive(Clone, PartialEq, ::prost::Message)]")
|| line.contains("#[derive(Clone, PartialEq, ::prost::Oneof)]")
|| line.contains("#[derive(Clone, Copy, PartialEq, ::prost::Message)]")
|| line.contains("#[derive(Clone, Copy, PartialEq, ::prost::Oneof)]")
{
// Messages: Remove PartialEq (manual impl in lib.rs)
if line.contains("#[derive(Clone, PartialEq, ::prost::Message)]") {
line.replace("PartialEq,", "")
} else if line.contains("#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]")
|| line.contains("#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Oneof)]")
|| line.contains("#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]")
|| line.contains("#[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)]")
{
} else if line.contains("#[derive(Clone, Copy, PartialEq, ::prost::Message)]") {
line.replace("PartialEq,", "")
} else if line.contains("#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]") {
line.replace("PartialEq, Eq, Hash,", "")
} else if line.contains("#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]") {
line.replace("PartialEq, Eq, Hash,", "")
}
// Oneofs: Remove PartialEq (manual impl in lib.rs), add Eq, Ord, PartialOrd
// For Oneofs with Hash: also remove Hash (VarInstance, UnfInstance have manual Hash)
else if line.contains("#[derive(Clone, PartialEq, ::prost::Oneof)]") {
line.replace(
"#[derive(Clone, PartialEq, ::prost::Oneof)]",
"#[derive(Clone, Eq, Ord, PartialOrd, ::prost::Oneof)]",
)
} else if line.contains("#[derive(Clone, Copy, PartialEq, ::prost::Oneof)]") {
line.replace(
"#[derive(Clone, Copy, PartialEq, ::prost::Oneof)]",
"#[derive(Clone, Copy, Eq, Ord, PartialOrd, ::prost::Oneof)]",
)
} else if line.contains("#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Oneof)]") {
// VarInstance: has manual Hash impl
line.replace(
"#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Oneof)]",
"#[derive(Clone, Copy, Eq, Ord, PartialOrd, ::prost::Oneof)]",
)
} else if line.contains("#[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)]") {
// UnfInstance: has manual Hash impl
line.replace(
"#[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)]",
"#[derive(Clone, Eq, Ord, PartialOrd, ::prost::Oneof)]",
)
} else {
line.to_string()
}
Expand Down
167 changes: 167 additions & 0 deletions models/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ impl PartialEq for Par {
&& self.bundles == other.bundles
&& self.connectives == other.connectives
&& self.connective_used == other.connective_used
&& self.use_blocks == other.use_blocks // Reifying RSpaces
}
}

Expand All @@ -81,6 +82,7 @@ impl Hash for Par {
self.bundles.hash(state);
self.connectives.hash(state);
self.connective_used.hash(state);
self.use_blocks.hash(state); // Reifying RSpaces
}
}

Expand Down Expand Up @@ -227,6 +229,7 @@ impl PartialEq for Send {
&& self.data == other.data
&& self.persistent == other.persistent
&& self.connective_used == other.connective_used
&& self.hyperparams == other.hyperparams
}
}

Expand All @@ -236,6 +239,63 @@ impl Hash for Send {
self.data.hash(state);
self.persistent.hash(state);
self.connective_used.hash(state);
self.hyperparams.hash(state);
}
}

impl PartialEq for Hyperparam {
fn eq(&self, other: &Self) -> bool {
self.hyperparam_instance == other.hyperparam_instance
}
}

impl Hash for Hyperparam {
fn hash<H: Hasher>(&self, state: &mut H) {
self.hyperparam_instance.hash(state);
}
}

impl PartialEq for hyperparam::HyperparamInstance {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(
hyperparam::HyperparamInstance::Positional(p1),
hyperparam::HyperparamInstance::Positional(p2),
) => p1 == p2,
(
hyperparam::HyperparamInstance::Named(n1),
hyperparam::HyperparamInstance::Named(n2),
) => n1 == n2,
_ => false,
}
}
}

impl Hash for hyperparam::HyperparamInstance {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
hyperparam::HyperparamInstance::Positional(p) => {
0u8.hash(state);
p.hash(state);
}
hyperparam::HyperparamInstance::Named(n) => {
1u8.hash(state);
n.hash(state);
}
}
}
}

impl PartialEq for NamedHyperparam {
fn eq(&self, other: &Self) -> bool {
self.key == other.key && self.value == other.value
}
}

impl Hash for NamedHyperparam {
fn hash<H: Hasher>(&self, state: &mut H) {
self.key.hash(state);
self.value.hash(state);
}
}

Expand All @@ -245,6 +305,7 @@ impl PartialEq for ReceiveBind {
&& self.source == other.source
&& self.remainder == other.remainder
&& self.free_count == other.free_count
&& self.pattern_modifiers == other.pattern_modifiers
}
}

Expand All @@ -254,6 +315,7 @@ impl Hash for ReceiveBind {
self.source.hash(state);
self.remainder.hash(state);
self.free_count.hash(state);
self.pattern_modifiers.hash(state);
}
}

Expand Down Expand Up @@ -404,6 +466,8 @@ impl PartialEq for expr::ExprInstance {
(ExprInstance::EPlusPlusBody(a), ExprInstance::EPlusPlusBody(b)) => a == b,
(ExprInstance::EMinusMinusBody(a), ExprInstance::EMinusMinusBody(b)) => a == b,
(ExprInstance::EModBody(a), ExprInstance::EModBody(b)) => a == b,
(ExprInstance::EFreeBody(a), ExprInstance::EFreeBody(b)) => a == b,
(ExprInstance::EFunctionBody(a), ExprInstance::EFunctionBody(b)) => a == b,
_ => false,
}
}
Expand Down Expand Up @@ -444,6 +508,8 @@ impl Hash for expr::ExprInstance {
ExprInstance::EPlusPlusBody(a) => a.hash(state),
ExprInstance::EMinusMinusBody(a) => a.hash(state),
ExprInstance::EModBody(a) => a.hash(state),
ExprInstance::EFreeBody(a) => a.hash(state),
ExprInstance::EFunctionBody(a) => a.hash(state),
}
}
}
Expand Down Expand Up @@ -831,6 +897,34 @@ impl Hash for EMinusMinus {
}
}

impl PartialEq for EFree {
fn eq(&self, other: &Self) -> bool {
self.body == other.body
}
}

impl Hash for EFree {
fn hash<H: Hasher>(&self, state: &mut H) {
self.body.hash(state);
}
}

impl PartialEq for EFunction {
fn eq(&self, other: &Self) -> bool {
self.function_name == other.function_name
&& self.arguments == other.arguments
&& self.connective_used == other.connective_used
}
}

impl Hash for EFunction {
fn hash<H: Hasher>(&self, state: &mut H) {
self.function_name.hash(state);
self.arguments.hash(state);
self.connective_used.hash(state);
}
}

impl PartialEq for Connective {
fn eq(&self, other: &Self) -> bool {
self.connective_instance == other.connective_instance
Expand Down Expand Up @@ -972,6 +1066,25 @@ impl Hash for GPrivate {
}
}

// Reifying RSpaces: UseBlock PartialEq and Hash implementations
impl PartialEq for UseBlock {
fn eq(&self, other: &Self) -> bool {
self.space == other.space
&& self.body == other.body
&& self.locally_free == other.locally_free
&& self.connective_used == other.connective_used
}
}

impl Hash for UseBlock {
fn hash<H: Hasher>(&self, state: &mut H) {
self.space.hash(state);
self.body.hash(state);
self.locally_free.hash(state);
self.connective_used.hash(state);
}
}

impl PartialEq for GDeployId {
fn eq(&self, other: &Self) -> bool {
self.sig == other.sig
Expand Down Expand Up @@ -1016,3 +1129,57 @@ impl fmt::Display for ServiceError {

// Implement the Error trait
impl Error for ServiceError {}

// ==========================================================================
// Channel Generation Support
// ==========================================================================

/// Implement `From<usize>` for `Par` to enable channel generation in GenericRSpace.
///
/// This creates a `Par` containing a `GPrivate` unforgeable channel with an ID
/// derived from the provided `usize` counter. This is used by channel stores
/// that need to generate unique channel names (e.g., `gensym`).
///
/// # Example
/// ```ignore
/// let channel: Par = 42usize.into();
/// // Creates Par { unforgeables: [GUnforgeable { GPrivateBody(GPrivate { id: [0,0,0,0,0,0,0,42] }) }] }
/// ```
impl From<usize> for Par {
fn from(id: usize) -> Self {
Par {
unforgeables: vec![GUnforgeable {
unf_instance: Some(UnfInstance::GPrivateBody(GPrivate {
// Convert usize to big-endian bytes for consistent channel IDs
id: id.to_be_bytes().to_vec(),
})),
}],
..Default::default()
}
}
}

/// Implement `AsRef<[u8]>` for `Par` to satisfy GenericRSpace trait bounds.
///
/// This implementation is used when Par is the channel type in GenericRSpace.
/// For spaces using HashMap storage (like VectorDB spaces), prefix semantics
/// are not used, so this returns an empty slice. For PathMap-based spaces,
/// a proper byte representation would be needed.
///
/// Note: This is a compatibility shim. For full prefix semantics support,
/// Par should encode its identity to bytes (e.g., via protobuf encoding).
impl AsRef<[u8]> for Par {
fn as_ref(&self) -> &[u8] {
// Return a reference to the first unforgeable's ID bytes if available,
// otherwise return an empty slice. This works for GPrivate channels
// created via From<usize>.
if let Some(unf) = self.unforgeables.first() {
if let Some(UnfInstance::GPrivateBody(private)) = &unf.unf_instance {
return &private.id;
}
}
// For other Par types (non-unforgeable channels), return empty slice.
// This is safe because HashMap stores don't use prefix semantics.
&[]
}
}
Loading