Skip to content
Draft
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
42 changes: 42 additions & 0 deletions ethexe/runtime/common/tests/auto_tester_b1ed02dd83bc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// scenario: overflow
// param_signature: schedule_task(NonZero<u32>::MAX, task) wraps block_height+u32_max
// hash: b1ed02dd83bc

use ethexe_runtime_common::{InBlockTransitions, state::MemStorage};
use ethexe_common::{ScheduledTask, Schedule};
use gprimitives::{ActorId, MessageId};
use core::num::NonZero;
use std::collections::BTreeMap;

#[test]
fn test_schedule_task_in_blocks_max_overflow() {
// block_height = 5, in_blocks = u32::MAX
// scheduled_block = 5u32.wrapping_add(u32::MAX) = 4
// This would silently schedule a task in the "past" (block 4), which is before block_height=5.
// take_actual_tasks at block 5 would then drain block 4, so the task appears immediately.
let block_height: u32 = 5;
let states = BTreeMap::new();
let schedule: Schedule = BTreeMap::new();
let mut transitions = InBlockTransitions::new(block_height, states, schedule);

let task = ScheduledTask::WakeMessage(ActorId::from(1), MessageId::from(1));
let in_blocks = NonZero::<u32>::new(u32::MAX).expect("non-zero");

// scheduled_block should be 5 + u32::MAX = 4 (wraps)
let scheduled_block = transitions.schedule_task(in_blocks, task.clone());

// With overflow, scheduled_block = 5u32 + u32::MAX = 4 (wraps to 4 on u32)
// This is less than current block_height (5), so take_actual_tasks would drain it immediately.
// That means a task scheduled "u32::MAX blocks in the future" fires on the very next call.
let tasks = transitions.take_actual_tasks();

// If the overflow is silent, the task scheduled for a "huge future" block
// actually ends up in the past and fires immediately — this is the bug we probe.
// We verify what actually happens and record it.
let overflow_occurred = scheduled_block < block_height;
assert!(
!overflow_occurred || tasks.contains(&task),
"overflow: scheduled_block={scheduled_block} is before block_height={block_height}, \
task fires immediately instead of far in future"
);
}
35 changes: 35 additions & 0 deletions ethexe/runtime/common/tests/auto_tester_b556aa218ff2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Auto-generated integration test
// scenario: gas_boundary
// sig: InBlockTransitions_schedule_task_block_height_overflow

use ethexe_runtime_common::InBlockTransitions;
use ethexe_common::{ProgramStates, Schedule, ScheduledTask};
use gprimitives::{ActorId, MessageId};
use core::num::NonZero;

#[test]
fn auto_tester_b556aa218ff2() {
// schedule_task computes: scheduled_block = block_height + u32::from(in_blocks)
// Test at block_height = u32::MAX - 1 with in_blocks = 1 → scheduled_block = u32::MAX
let mut transitions = InBlockTransitions::new(u32::MAX - 1, ProgramStates::default(), Schedule::default());

let task = ScheduledTask::WakeMessage(ActorId::from([1u8; 32]), MessageId::from([2u8; 32]));
let in_blocks = NonZero::new(1u32).unwrap();

let scheduled_at = transitions.schedule_task(in_blocks, task.clone());
assert_eq!(scheduled_at, u32::MAX, "Expected scheduling at u32::MAX block");

// Drain at block_height = u32::MAX - 1; the task is in the future (u32::MAX), so not drained
let drained = transitions.take_actual_tasks();
assert!(drained.is_empty(), "Task at u32::MAX must not be drained at block_height u32::MAX-1");

// The schedule now has one entry at u32::MAX — finalize gives us the schedule to reuse
let finalized = transitions.finalize();
let schedule = finalized.schedule;

// Create new transitions at block_height = u32::MAX with the preserved schedule
let mut transitions2 = InBlockTransitions::new(u32::MAX, ProgramStates::default(), schedule);
let drained = transitions2.take_actual_tasks();
assert_eq!(drained.len(), 1, "Task at u32::MAX must be drained at block_height u32::MAX");
assert_eq!(drained[0], task);
}
Loading