Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions Cargo.lock

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

16 changes: 14 additions & 2 deletions core/processor/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,18 +151,30 @@ impl ProcessorContext {
}
}

/// Message execution result info
#[derive(Debug)]
pub struct ExtInfo {
/// Gas amount left after execution.
pub gas_amount: GasAmount,
/// Gas reserver with updated reservations after execution.
pub gas_reserver: GasReserver,
/// System reservation context with current and previous reservations.
pub system_reservation_context: SystemReservationContext,
/// Whether allocations were changed during execution and final state of allocations if they were changed.
pub allocations: Option<IntervalsTree<WasmPage>>,
/// Data of accessed pages during execution.
pub pages_data: BTreeMap<GearPage, PageBuf>,
/// List of generated dispatches with their delay and optional reservation id.
pub generated_dispatches: Vec<(Dispatch, u32, Option<ReservationId>)>,
/// List of wakened messages with their id and delay until awakening.
pub awakening: Vec<(MessageId, u32)>,
/// List of reply deposits with message id and amount.
pub reply_deposits: Vec<(MessageId, u64)>,
/// Programs to create data.
pub program_candidates_data: BTreeMap<CodeId, Vec<(MessageId, ActorId)>>,
/// Executed message context store after execution.
pub context_store: ContextStore,
/// Whether reply was sent during execution.
pub reply_sent: bool,
}

Expand Down Expand Up @@ -1225,7 +1237,7 @@ impl<LP: LazyPagesInterface> Externalities for Ext<LP> {
if let Some(reimbursement) = reimburse {
let current_gas_amount = self.gas_amount();

// Basically amount of the reseravtion and the cost for the hold duration.
// Basically amount of the reservation and the cost for the hold duration.
Comment thread
grishasobol marked this conversation as resolved.
let reimbursement_amount = self.cost_for_reservation(amount, reimbursement.duration());
self.context
.gas_counter
Expand Down Expand Up @@ -2324,7 +2336,7 @@ mod tests {
.build(),
);

// Check all the reseravations are in "existing" state.
// Check all the reservations are in "existing" state.
assert!(
ext.context
.gas_reserver
Expand Down
4 changes: 3 additions & 1 deletion core/processor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ mod processing;

pub use context::{ProcessExecutionContext, SystemReservationContext};
pub use ext::{
AllocExtError, Ext, FallibleExtError, ProcessorContext, ProcessorExternalities,
AllocExtError, Ext, ExtInfo, FallibleExtError, ProcessorContext, ProcessorExternalities,
UnrecoverableExtError,
};
pub use gear_core::{env::Externalities, gas::CountersOwner};
pub use gear_core_backend::BackendExternalities;
pub use handler::handle_journal;
pub use precharge::*;
pub use processing::{
Expand Down
174 changes: 172 additions & 2 deletions ethexe/processor/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::*;
use anyhow::{Result, anyhow};
use ethexe_common::{
DEFAULT_BLOCK_GAS_LIMIT, OUTGOING_MESSAGES_SOFT_LIMIT, PROGRAM_MODIFICATIONS_SOFT_LIMIT,
SimpleBlockData,
ScheduledTask, SimpleBlockData,
db::*,
events::{
BlockRequestEvent, MirrorRequestEvent, RouterRequestEvent,
Expand All @@ -29,7 +29,7 @@ use ethexe_common::{
},
mock::*,
};
use ethexe_runtime_common::{RUNTIME_ID, state::MessageQueue};
use ethexe_runtime_common::{RUNTIME_ID, WAIT_UP_TO_SAFE_DURATION, state::MessageQueue};
use gear_core::{
ids::prelude::CodeIdExt,
message::{ErrorReplyReason, ReplyCode, SuccessReplyReason},
Expand Down Expand Up @@ -133,6 +133,47 @@ mod utils {
salt: H256::random().0.to_vec().try_into().unwrap(),
}
}

pub async fn simple_init_test(code: impl AsRef<[u8]>) -> InBlockTransitions {
let (mut processor, chain, [code_id]) = setup_test_env_and_load_codes([code.as_ref()]);
let block1 = chain.blocks[1].to_simple();

let mut handler = setup_handler(processor.db.clone(), block1);
let actor_id = ActorId::from(0x10000);
handler
.handle_router_event(RouterRequestEvent::ProgramCreated(ProgramCreatedEvent {
actor_id,
code_id,
}))
.expect("failed to create new program");
handler
.handle_mirror_event(
actor_id,
MirrorRequestEvent::ExecutableBalanceTopUpRequested(
ExecutableBalanceTopUpRequestedEvent {
value: 350_000_000_000,
},
),
)
.expect("failed to top up balance");
handler
.handle_mirror_event(
actor_id,
MirrorRequestEvent::MessageQueueingRequested(MessageQueueingRequestedEvent {
id: MessageId::from(1),
source: ActorId::from(10),
payload: vec![],
value: 0,
call_reply: false,
}),
)
.expect("failed to queue message");

processor
.process_queues(handler.transitions, block1, DEFAULT_BLOCK_GAS_LIMIT, None)
.await
.unwrap()
}
}

#[tokio::test(flavor = "multi_thread")]
Expand Down Expand Up @@ -1382,3 +1423,132 @@ async fn insufficient_executable_balance_still_charged() {
let exec_balance_after = handler.program_state(actor_id).executable_balance;
assert!(exec_balance_after < INSUFFICIENT_EXECUTABLE_BALANCE);
}

#[tokio::test(flavor = "multi_thread")]
async fn call_gr_wait_is_forbidden() {
init_logger();

let wat = format!(
r#"
(module
(import "env" "memory" (memory 0))
(import "env" "gr_wait" (func $wait))
(export "init" (func $init))
(func $init call $wait)
)
"#
);

let transitions = simple_init_test(wat_to_wasm(&wat).1).await;
let reply_code = transitions.current_messages()[0]
.1
.reply_details
.expect("must be reply")
.to_reply_code();
assert_eq!(
reply_code,
ReplyCode::Error(ErrorReplyReason::Execution(
SimpleExecutionError::BackendError
)),
"Forbidden syscall should return backend error"
);
}

#[tokio::test(flavor = "multi_thread")]
async fn call_wake_with_delay_is_unsupported() {
init_logger();

let get_wat = |delay: u32| {
format!(
r#"
(module
(import "env" "memory" (memory 1))
(import "env" "gr_wake" (func $wake (param i32 i32 i32)))
(export "init" (func $init))
(func $init
(call $wake (i32.const 0) (i32.const {delay}) (i32.const 0))
(if (i32.eqz (i32.load (i32.const 0x0)))
(then nop)
(else unreachable)
)
)
)
"#
)
};

// with delay != 0
let wat = get_wat(10);
let transitions = simple_init_test(wat_to_wasm(&wat).1).await;
let reply_code = transitions.current_messages()[0]
.1
.reply_details
.expect("must be reply")
.to_reply_code();
assert_eq!(
reply_code,
ReplyCode::Error(ErrorReplyReason::Execution(
SimpleExecutionError::UnreachableInstruction
)),
"Calling gr_wake with non-zero delay should lead to unreachable instruction"
);

// with delay == 0
let wat = get_wat(0);
let transitions = simple_init_test(wat_to_wasm(&wat).1).await;
let reply_code = transitions.current_messages()[0]
.1
.reply_details
.expect("must be reply")
.to_reply_code();
assert_eq!(reply_code, ReplyCode::Success(SuccessReplyReason::Auto));
}

#[tokio::test(flavor = "multi_thread")]
async fn call_wait_up_to_with_huge_delay() {
init_logger();

let get_wat = |duration: u32| {
format!(
r#"
(module
(import "env" "memory" (memory 1))
(import "env" "gr_wait_up_to" (func $wait_up_to (param i32)))
(export "init" (func $init))
(func $init
(call $wait_up_to (i32.const {duration}))
)
)
"#
)
};

// Huge duration
let wat = get_wat(0xFFFFFFFF);
let transitions = simple_init_test(wat_to_wasm(&wat).1).await;
let block_height = transitions.block_height();
let FinalizedBlockTransitions { schedule, .. } = transitions.finalize();
let (block, tasks) = schedule.into_iter().next().unwrap();
assert_eq!(
block,
block_height + WAIT_UP_TO_SAFE_DURATION,
"Delay should be capped to WAIT_UP_TO_SAFE_DURATION"
);
let task = tasks.into_iter().next().unwrap();
assert!(matches!(task, ScheduledTask::WakeMessage(_, _)));

// Normal duration
let duration = WAIT_UP_TO_SAFE_DURATION + 20;
let wat = get_wat(duration);
let transitions = simple_init_test(wat_to_wasm(&wat).1).await;
let block_height = transitions.block_height();
let FinalizedBlockTransitions { schedule, .. } = transitions.finalize();
let (block, tasks) = schedule.into_iter().next().unwrap();
assert_eq!(
block,
block_height + duration,
"Delay should not be capped if it's less than WAIT_UP_TO_SAFE_DURATION"
);
let task = tasks.into_iter().next().unwrap();
assert!(matches!(task, ScheduledTask::WakeMessage(_, _)));
}
1 change: 1 addition & 0 deletions ethexe/runtime/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ derive_more.workspace = true
auto_impl.workspace = true
serde = { workspace = true, features = ["derive"], optional = true }
gear-workspace-hack.workspace = true
delegate.workspace = true

[features]
default = ["std"]
Expand Down
Loading
Loading