Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
d9124e0
fix: resolve observer COST_MISMATCH during block replay
dylon Mar 27, 2026
4d63ce1
fix: resolve test failures and add coverage for COST_MISMATCH fix
dylon Mar 31, 2026
3c82596
Merge remote-tracking branch 'origin/rust/dev' into dylon/embers-demo…
dylon Mar 31, 2026
84463d3
fix: remove dead code and unused imports to satisfy -D warnings
dylon Mar 31, 2026
0b7a5d6
fix: remove unused register_lock_file stub to satisfy -D dead-code
dylon Mar 31, 2026
4ac069b
fix: set 32MB stack size on tokio worker threads to prevent overflow
dylon Mar 31, 2026
f3b8e45
refactor: atomic cost manager and order-independent cost accounting (…
dylon Apr 1, 2026
a1aef94
refactor: remove hot store outer Mutex, expose DashMap concurrency (P…
dylon Apr 1, 2026
be26e1b
refactor: change ISpace trait from &mut self to &self (Phase 3)
dylon Apr 1, 2026
550132d
refactor: per-channel-group locks for join pattern atomicity (Phase 4)
dylon Apr 1, 2026
4e92c06
refactor: remove interpreter-level space lock (Phase 5)
dylon Apr 1, 2026
2d26852
refactor: content-hash ordering, FuturesUnordered, two-phase dispatch…
dylon Apr 2, 2026
65ecc69
docs: add concurrent RSpace architecture technical report
dylon Apr 3, 2026
69ed5bd
Fixes minor type in Rholang syntax
dylon Apr 3, 2026
5e8308d
Fixes minor type in Rholang syntax
dylon Apr 3, 2026
715091b
Fixes minor type in Rholang syntax
dylon Apr 3, 2026
5af2042
Minor formatting fix
dylon Apr 3, 2026
b337777
Adds section about possible future optimization to partition independ…
dylon Apr 3, 2026
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
11 changes: 7 additions & 4 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
# This file contains build configuration and compiler flags for the entire workspace.

[env]
# Set minimum thread stack size to 8MB for test threads.
# Set minimum thread stack size to 32MB for test threads.
# The Rholang interpreter uses deep async recursion (eval → produce/consume → dispatch → eval)
# via Box::pin patterns. In debug builds, each recursion level consumes significantly more
# stack space (~1-2KB) than in release builds (~100-200 bytes) due to lack of inlining and
# unoptimized async state machines. The default 2MB stack overflows during normal test execution.
# See: https://github.com/F1R3FLY-io/f1r3node/issues/305
RUST_MIN_STACK = "8388608"
# unoptimized async state machines. With receives-first evaluation ordering, genesis contracts
# create deeper COMM cascades (50+ levels), requiring more thread stack than the stacker crate
# can compensate for via heap-allocated segments.
# History: 2MB (default) → 8MB (issue #305) → 32MB (receives-first evaluation).
# Long-term fix: convert recursive async calls to trampolines.
RUST_MIN_STACK = "33554432"

[build]
# Enable native CPU features for gxhash (requires AES and SSE2 intrinsics)
Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions casper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ k256 = { version = "0.13.4", features = ["ecdsa"] }
serial_test = "3.1"
env_logger = "0.11.8"
tempfile = "3.24.0"
libc = "0.2"



Expand Down
33 changes: 32 additions & 1 deletion casper/src/rust/api/block_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,18 @@ impl BlockAPI {
}
}
ProposerResult::Empty => {
tracing::debug!("Propose already in progress");
if attempt < max_attempts {
tracing::debug!(
"Propose already in progress (attempt {}/{}); retrying in {:?}",
attempt,
max_attempts,
retry_delay
);
attempt += 1;
tokio::time::sleep(retry_delay).await;
continue;
}
tracing::debug!("Propose already in progress; max retries exhausted");
}
ProposerResult::Started(seq_number) => {
tracing::debug!("Propose started (seqNum {})", seq_number);
Expand Down Expand Up @@ -1471,6 +1482,7 @@ impl BlockAPI {
&snapshot,
&runtime_guard,
Some(true), // disable_late_block_filtering = true for exploratory deploy
None,
)?;
merged_state_hash
};
Expand Down Expand Up @@ -1507,11 +1519,30 @@ impl BlockAPI {

match target_block {
Some(b) => {
tracing::info!(
target: "f1r3fly.rholang.diag",
state_hash = %PrettyPrinter::build_string_bytes(&state_hash),
block_number = b.body.state.block_number,
block_hash = %PrettyPrinter::build_string_bytes(&b.block_hash),
parent_hash = %b.header.parents_hash_list.first()
.map(|h| PrettyPrinter::build_string_bytes(h))
.unwrap_or_default(),
deploy_count = b.body.deploys.len(),
"exploratory_deploy: LFB details for state selection"
);
let res = runtime_manager
.lock()
.await
.play_exploratory_deploy(term, &state_hash)
.await?;
tracing::info!(
target: "f1r3fly.rholang.diag",
state_hash = %PrettyPrinter::build_string_bytes(&state_hash),
block_number = b.body.state.block_number,
result_count = res.len(),
"exploratory_deploy: play_exploratory_deploy returned {} result pars",
res.len()
);
let light_block_info =
Self::get_light_block_info(casper.as_ref(), &b).await?;
Ok((res, light_block_info))
Expand Down
4 changes: 3 additions & 1 deletion casper/src/rust/blocks/block_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ use shared::rust::env;

use crate::rust::block_status::BlockError;
use crate::rust::engine::block_retriever::{AdmitHashReason, BlockRetriever};
#[cfg(all(target_os = "linux", target_env = "gnu"))]
use crate::rust::metrics_constants::ALLOCATOR_TRIM_TOTAL_METRIC;
use crate::rust::metrics_constants::{
ALLOCATOR_TRIM_TOTAL_METRIC, BLOCK_PROCESSING_STORAGE_TIME_METRIC,
BLOCK_PROCESSING_STORAGE_TIME_METRIC,
BLOCK_PROCESSING_VALIDATION_SETUP_TIME_METRIC, BLOCK_PROCESSOR_METRICS_SOURCE,
BLOCK_SIZE_METRIC, BLOCK_VALIDATION_FAILED_METRIC, BLOCK_VALIDATION_SUCCESS_METRIC,
BLOCK_VALIDATION_TIME_METRIC,
Expand Down
8 changes: 6 additions & 2 deletions casper/src/rust/engine/block_approver_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,12 @@ impl<T: TransportLayer + Send + Sync + 'static> BlockApproverProtocol<T> {
));
}

// State hash checks
let empty_state_hash = RuntimeManager::empty_state_hash_fixed();
// State hash checks — use the genesis block's own pre-state hash
// rather than a hardcoded constant. The pre-state hash is dynamically
// computed during genesis creation and is guaranteed to be in the
// roots store. A hardcoded constant breaks when evaluation order or
// trie hashing changes.
let empty_state_hash = block.body.state.pre_state_hash.clone();
let state_hash = runtime_manager
.replay_compute_state(
&empty_state_hash,
Expand Down
5 changes: 3 additions & 2 deletions casper/src/rust/engine/initializing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -790,8 +790,9 @@ impl<T: TransportLayer + Send + Sync + Clone> Initializing<T> {
let system_deploys = proto_util::system_deploys(block);
let block_data = rholang::rust::interpreter::system_processes::BlockData::from_block(block);

// Genesis starts from empty state
let pre_state_hash = RuntimeManager::empty_state_hash_fixed();
// Use the genesis block's own pre-state hash rather than a hardcoded
// constant — see block_approver_protocol.rs for rationale.
let pre_state_hash = block.body.state.pre_state_hash.clone();

// Replay genesis - this will save mergeable channels to the store
let mut runtime_manager = self.runtime_manager.lock().await;
Expand Down
17 changes: 17 additions & 0 deletions casper/src/rust/rholang/replay_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@ impl ReplayRuntimeOps {
let checkpoint_start = Instant::now();
tracing::debug!(target: "f1r3fly.casper.replay-rho-runtime", "create-checkpoint-started");
let checkpoint = self.runtime_ops.runtime.create_checkpoint();
tracing::info!(
target: "f1r3fly.rspace",
replay_root = %hex::encode(checkpoint.root.bytes()),
"replay_deploys: checkpoint completed"
);
tracing::debug!(target: "f1r3fly.casper.replay-rho-runtime", "create-checkpoint-finished");
metrics::histogram!(BLOCK_REPLAY_PHASE_CREATE_CHECKPOINT_TIME_METRIC, "source" => CASPER_METRICS_SOURCE)
.record(checkpoint_start.elapsed().as_secs_f64());
Expand Down Expand Up @@ -324,6 +329,7 @@ impl ReplayRuntimeOps {
let deploy_data = SystemProcessDeployData::from_deploy(&processed_deploy.deploy);
self.runtime_ops.runtime.set_deploy_data(deploy_data).await;

rholang::rust::interpreter::storage::charging_rspace::reset_cost_trace_seq();
let mut user_eval_result = self.runtime_ops.evaluate(&processed_deploy.deploy).await?;
self.discard_event_log("user-deploy", false);

Expand All @@ -347,6 +353,17 @@ impl ReplayRuntimeOps {
}

if processed_deploy.cost.cost != user_eval_result.cost.value as u64 {
tracing::error!(
target: "f1r3fly.rspace.cost_trace",
initial_cost = processed_deploy.cost.cost,
replay_cost = user_eval_result.cost.value,
cost_diff = processed_deploy.cost.cost as i64 - user_eval_result.cost.value,
deploy_sig = %hex::encode(&processed_deploy.deploy.sig),
"COST_MISMATCH_DETAIL: validator_cost={} observer_cost={} diff={}",
processed_deploy.cost.cost,
user_eval_result.cost.value,
processed_deploy.cost.cost as i64 - user_eval_result.cost.value
);
return Err(CasperError::ReplayFailure(
ReplayFailure::replay_cost_mismatch(
processed_deploy.cost.cost,
Expand Down
Loading