Skip to content

Commit 69e94ce

Browse files
barak-b-starkwareAvivYossef-starkwareYoni-Starkwareavi-starkwareilyalesokhin-starkware
authored
Merge pull request starkware-libs#2098 from starkware-libs/barak/merge-main-v0.13.2-into-main
Merge main-v0.13.2 into main (starkware-libs#2098) Co-Authored-By: AvivYossef-starkware <[email protected]> Co-Authored-By: Yoni <[email protected]> Co-Authored-By: avi-starkware <[email protected]> Co-Authored-By: ilyalesokhin-starkware <[email protected]> Co-Authored-By: Meshi Peled <[email protected]>
2 parents 9bfb3d4 + a03232e commit 69e94ce

20 files changed

+1164
-646
lines changed

crates/blockifier/feature_contracts/cairo1/compiled/test_contract.casm.json

+877-615
Large diffs are not rendered by default.

crates/blockifier/feature_contracts/cairo1/test_contract.cairo

+16
Original file line numberDiff line numberDiff line change
@@ -553,4 +553,20 @@ mod TestContract {
553553

554554
assert!(outputs.get_output(mul) == u384 { limb0: 6, limb1: 0, limb2: 0, limb3: 0 });
555555
}
556+
557+
558+
// Add drop for AddInputResult as it only has PanicDestruct.
559+
impl AddInputResultDrop<C> of Drop<core::circuit::AddInputResult<C>>;
560+
561+
#[external(v0)]
562+
fn test_rc96_holes(ref self: ContractState) {
563+
test_rc96_holes_helper();
564+
test_rc96_holes_helper();
565+
}
566+
567+
#[inline(never)]
568+
fn test_rc96_holes_helper() {
569+
let in1 = CircuitElement::<CircuitInput<0>> {};
570+
(in1,).new_inputs().next([3, 0, 0, 0]);
571+
}
556572
}

crates/blockifier/resources/versioned_constants.json

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
},
2626
"disable_cairo0_redeclaration": true,
2727
"max_recursion_depth": 50,
28+
"segment_arena_cells": false,
2829
"os_constants": {
2930
"block_hash_contract_address": 1,
3031
"call_contract_gas_cost": {

crates/blockifier/resources/versioned_constants_13_0.json

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
},
66
"invoke_tx_max_n_steps": 3000000,
77
"max_recursion_depth": 50,
8+
"segment_arena_cells": true,
89
"os_constants": {
910
"nop_entry_point_offset": -1,
1011
"entry_point_type_external": 0,

crates/blockifier/resources/versioned_constants_13_1.json

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
]
2525
},
2626
"max_recursion_depth": 50,
27+
"segment_arena_cells": true,
2728
"os_constants": {
2829
"nop_entry_point_offset": -1,
2930
"entry_point_type_external": 0,

crates/blockifier/resources/versioned_constants_13_1_1.json

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
]
2525
},
2626
"max_recursion_depth": 50,
27+
"segment_arena_cells": true,
2728
"os_constants": {
2829
"nop_entry_point_offset": -1,
2930
"entry_point_type_external": 0,

crates/blockifier/src/blockifier/transaction_executor.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#[cfg(feature = "concurrency")]
22
use std::collections::{HashMap, HashSet};
33
#[cfg(feature = "concurrency")]
4+
use std::panic::{self, catch_unwind, AssertUnwindSafe};
5+
#[cfg(feature = "concurrency")]
46
use std::sync::Arc;
57
#[cfg(feature = "concurrency")]
68
use std::sync::Mutex;
@@ -218,6 +220,8 @@ impl<S: StateReader + Send + Sync> TransactionExecutor<S> {
218220
&mut self,
219221
chunk: &[Transaction],
220222
) -> Vec<TransactionExecutorResult<TransactionExecutionInfo>> {
223+
use crate::concurrency::utils::AbortIfPanic;
224+
221225
let block_state = self.block_state.take().expect("The block state should be `Some`.");
222226

223227
let worker_executor = Arc::new(WorkerExecutor::initialize(
@@ -236,7 +240,24 @@ impl<S: StateReader + Send + Sync> TransactionExecutor<S> {
236240
for _ in 0..self.config.concurrency_config.n_workers {
237241
let worker_executor = Arc::clone(&worker_executor);
238242
s.spawn(move || {
239-
worker_executor.run();
243+
// Making sure that the program will abort if a panic accured while halting the
244+
// scheduler.
245+
let abort_guard = AbortIfPanic;
246+
// If a panic is not handled or the handling logic itself panics, then we abort
247+
// the program.
248+
if let Err(err) = catch_unwind(AssertUnwindSafe(|| {
249+
worker_executor.run();
250+
})) {
251+
// If the program panics here, the abort guard will exit the program.
252+
// In this case, no panic message will be logged. Add the cargo flag
253+
// --nocapture to log the panic message.
254+
255+
worker_executor.scheduler.halt();
256+
abort_guard.release();
257+
panic::resume_unwind(err);
258+
}
259+
260+
abort_guard.release();
240261
});
241262
}
242263
});

crates/blockifier/src/concurrency/scheduler.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ impl<'a> TransactionCommitter<'a> {
5252
assert!(*self.commit_index_guard > 0, "Commit index underflow.");
5353
*self.commit_index_guard -= 1;
5454

55-
self.scheduler.done_marker.store(true, Ordering::Release);
55+
self.scheduler.halt();
5656
}
5757
}
5858

@@ -161,6 +161,10 @@ impl Scheduler {
161161
*self.commit_index.lock().unwrap()
162162
}
163163

164+
pub fn halt(&self) {
165+
self.done_marker.store(true, Ordering::Release);
166+
}
167+
164168
fn lock_tx_status(&self, tx_index: TxIndex) -> MutexGuard<'_, TransactionStatus> {
165169
lock_mutex_in_array(&self.tx_statuses, tx_index)
166170
}

crates/blockifier/src/concurrency/utils.rs

+17
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,23 @@ use std::sync::{Mutex, MutexGuard};
33

44
use crate::concurrency::TxIndex;
55

6+
// This struct is used to abort the program if a panic occurred in a place where it could not be
7+
// handled.
8+
pub struct AbortIfPanic;
9+
10+
impl Drop for AbortIfPanic {
11+
fn drop(&mut self) {
12+
eprintln!("detected unexpected panic; aborting");
13+
std::process::abort();
14+
}
15+
}
16+
17+
impl AbortIfPanic {
18+
pub fn release(self) {
19+
std::mem::forget(self);
20+
}
21+
}
22+
623
pub fn lock_mutex_in_array<T: Debug>(array: &[Mutex<T>], tx_index: TxIndex) -> MutexGuard<'_, T> {
724
array[tx_index].lock().unwrap_or_else(|error| {
825
panic!("Cell of transaction index {} is poisoned. Data: {:?}.", tx_index, *error.get_ref())

crates/blockifier/src/execution/deprecated_entry_point_execution.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use cairo_vm::types::builtin_name::BuiltinName;
12
use cairo_vm::types::layout_name::LayoutName;
23
use cairo_vm::types::relocatable::{MaybeRelocatable, Relocatable};
34
use cairo_vm::vm::errors::vm_errors::VirtualMachineError;
@@ -6,6 +7,7 @@ use starknet_api::core::EntryPointSelector;
67
use starknet_api::deprecated_contract_class::EntryPointType;
78
use starknet_api::hash::StarkHash;
89

10+
use super::execution_utils::SEGMENT_ARENA_BUILTIN_SIZE;
911
use crate::abi::abi_utils::selector_from_name;
1012
use crate::abi::constants::{CONSTRUCTOR_ENTRY_POINT_NAME, DEFAULT_ENTRY_POINT_SELECTOR};
1113
use crate::execution::call_info::{CallExecution, CallInfo};
@@ -227,12 +229,18 @@ pub fn finalize_execution(
227229

228230
// Take into account the VM execution resources of the current call, without inner calls.
229231
// Has to happen after marking holes in segments as accessed.
230-
let vm_resources_without_inner_calls = runner
232+
let mut vm_resources_without_inner_calls = runner
231233
.get_execution_resources()
232234
.map_err(VirtualMachineError::RunnerError)?
233235
.filter_unused_builtins();
234-
*syscall_handler.resources += &vm_resources_without_inner_calls;
235236
let versioned_constants = syscall_handler.context.versioned_constants();
237+
if versioned_constants.segment_arena_cells {
238+
vm_resources_without_inner_calls
239+
.builtin_instance_counter
240+
.get_mut(&BuiltinName::segment_arena)
241+
.map_or_else(|| {}, |val| *val *= SEGMENT_ARENA_BUILTIN_SIZE);
242+
}
243+
*syscall_handler.resources += &vm_resources_without_inner_calls;
236244
// Take into account the syscall resources of the current call.
237245
*syscall_handler.resources += &versioned_constants
238246
.get_additional_os_syscall_resources(&syscall_handler.syscall_counter)?;

crates/blockifier/src/execution/entry_point_execution.rs

+87-8
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@ use std::collections::HashSet;
33
use cairo_vm::types::builtin_name::BuiltinName;
44
use cairo_vm::types::layout_name::LayoutName;
55
use cairo_vm::types::relocatable::{MaybeRelocatable, Relocatable};
6+
use cairo_vm::vm::errors::cairo_run_errors::CairoRunError;
7+
use cairo_vm::vm::errors::memory_errors::MemoryError;
68
use cairo_vm::vm::errors::vm_errors::VirtualMachineError;
9+
use cairo_vm::vm::runners::builtin_runner::BuiltinRunner;
710
use cairo_vm::vm::runners::cairo_runner::{CairoArg, CairoRunner, ExecutionResources};
8-
use num_traits::ToPrimitive;
11+
use cairo_vm::vm::security::verify_secure_runner;
12+
use num_traits::{ToPrimitive, Zero};
913
use starknet_api::felt;
1014
use starknet_types_core::felt::Felt;
1115

@@ -17,6 +21,7 @@ use crate::execution::entry_point::{
1721
use crate::execution::errors::{EntryPointExecutionError, PostExecutionError, PreExecutionError};
1822
use crate::execution::execution_utils::{
1923
read_execution_retdata, write_felt, write_maybe_relocatable, Args, ReadOnlySegments,
24+
SEGMENT_ARENA_BUILTIN_SIZE,
2025
};
2126
use crate::execution::syscalls::hint_processor::SyscallHintProcessor;
2227
use crate::state::state_api::State;
@@ -157,7 +162,6 @@ pub fn initialize_execution_context<'a>(
157162
BuiltinName::bitwise,
158163
BuiltinName::ec_op,
159164
BuiltinName::ecdsa,
160-
BuiltinName::output,
161165
BuiltinName::pedersen,
162166
BuiltinName::poseidon,
163167
BuiltinName::range_check,
@@ -280,17 +284,86 @@ pub fn run_entry_point(
280284
args: Args,
281285
program_segment_size: usize,
282286
) -> EntryPointExecutionResult<()> {
283-
let verify_secure = true;
287+
// Note that we run `verify_secure_runner` manually after filling the holes in the rc96 segment.
288+
let verify_secure = false;
284289
let args: Vec<&CairoArg> = args.iter().collect();
285-
let result = runner.run_from_entrypoint(
290+
runner.run_from_entrypoint(
286291
entry_point.pc(),
287292
&args,
288293
verify_secure,
289294
Some(program_segment_size),
290295
hint_processor,
291-
);
296+
)?;
297+
298+
maybe_fill_holes(entry_point, runner)?;
292299

293-
Ok(result?)
300+
verify_secure_runner(runner, false, Some(program_segment_size))
301+
.map_err(CairoRunError::VirtualMachine)?;
302+
303+
Ok(())
304+
}
305+
306+
/// Fills the holes after running the entry point.
307+
/// Currently only fills the holes in the rc96 segment.
308+
fn maybe_fill_holes(
309+
entry_point: EntryPointV1,
310+
runner: &mut CairoRunner,
311+
) -> Result<(), EntryPointExecutionError> {
312+
let Some(rc96_offset) = entry_point
313+
.builtins
314+
.iter()
315+
.rev()
316+
.position(|name| name.as_str() == BuiltinName::range_check96.to_str_with_suffix())
317+
else {
318+
return Ok(());
319+
};
320+
let rc96_builtin_runner = runner
321+
.vm
322+
.get_builtin_runners()
323+
.iter()
324+
.find_map(|builtin| {
325+
if let BuiltinRunner::RangeCheck96(rc96_builtin_runner) = builtin {
326+
Some(rc96_builtin_runner)
327+
} else {
328+
None
329+
}
330+
})
331+
.expect("RangeCheck96 builtin runner not found.");
332+
333+
// 'EntryPointReturnValues' is returned after the implicits and its size is 5,
334+
// So the last implicit is at offset 5 + 1.
335+
const IMPLICITS_OFFSET: usize = 6;
336+
let rc_96_stop_ptr = (runner.vm.get_ap() - (IMPLICITS_OFFSET + rc96_offset))
337+
.map_err(|err| CairoRunError::VirtualMachine(VirtualMachineError::Math(err)))?;
338+
339+
let rc96_base = rc96_builtin_runner.base();
340+
let rc96_segment: isize =
341+
rc96_base.try_into().expect("Builtin segment index must fit in isize.");
342+
343+
let Relocatable { segment_index: rc96_stop_segment, offset: stop_offset } =
344+
runner.vm.get_relocatable(rc_96_stop_ptr).map_err(CairoRunError::MemoryError)?;
345+
assert_eq!(rc96_stop_segment, rc96_segment);
346+
347+
// Update `segment_used_sizes` to include the holes.
348+
runner
349+
.vm
350+
.segments
351+
.segment_used_sizes
352+
.as_mut()
353+
.expect("Segments used sizes should be calculated at this point")[rc96_base] = stop_offset;
354+
355+
for offset in 0..stop_offset {
356+
match runner
357+
.vm
358+
.insert_value(Relocatable { segment_index: rc96_segment, offset }, Felt::zero())
359+
{
360+
// If the value is already set, ignore the error.
361+
Ok(()) | Err(MemoryError::InconsistentMemory(_)) => {}
362+
Err(err) => panic!("Unexpected error when filling holes: {err}."),
363+
}
364+
}
365+
366+
Ok(())
294367
}
295368

296369
pub fn finalize_execution(
@@ -319,12 +392,18 @@ pub fn finalize_execution(
319392

320393
// Take into account the VM execution resources of the current call, without inner calls.
321394
// Has to happen after marking holes in segments as accessed.
322-
let vm_resources_without_inner_calls = runner
395+
let mut vm_resources_without_inner_calls = runner
323396
.get_execution_resources()
324397
.map_err(VirtualMachineError::RunnerError)?
325398
.filter_unused_builtins();
326-
*syscall_handler.resources += &vm_resources_without_inner_calls;
327399
let versioned_constants = syscall_handler.context.versioned_constants();
400+
if versioned_constants.segment_arena_cells {
401+
vm_resources_without_inner_calls
402+
.builtin_instance_counter
403+
.get_mut(&BuiltinName::segment_arena)
404+
.map_or_else(|| {}, |val| *val *= SEGMENT_ARENA_BUILTIN_SIZE);
405+
}
406+
*syscall_handler.resources += &vm_resources_without_inner_calls;
328407
// Take into account the syscall resources of the current call.
329408
*syscall_handler.resources += &versioned_constants
330409
.get_additional_os_syscall_resources(&syscall_handler.syscall_counter)?;

crates/blockifier/src/execution/entry_point_test.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -529,12 +529,11 @@ fn test_cairo1_entry_point_segment_arena() {
529529
..trivial_external_entry_point_new(test_contract)
530530
};
531531

532-
assert!(
533-
entry_point_call
534-
.execute_directly(&mut state)
535-
.unwrap()
536-
.resources
537-
.builtin_instance_counter
538-
.contains_key(&BuiltinName::segment_arena)
532+
assert_eq!(
533+
entry_point_call.execute_directly(&mut state).unwrap().resources.builtin_instance_counter
534+
[&BuiltinName::segment_arena],
535+
// Note: the number of segment_arena instances should not depend on the compiler or VM
536+
// version. Do not manually fix this then when upgrading them - it might be a bug.
537+
2
539538
);
540539
}

crates/blockifier/src/execution/execution_utils.rs

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ use crate::transaction::objects::TransactionInfo;
3434

3535
pub type Args = Vec<CairoArg>;
3636

37+
pub const SEGMENT_ARENA_BUILTIN_SIZE: usize = 3;
38+
3739
/// Executes a specific call to a contract entry point and returns its output.
3840
pub fn execute_entry_point_call(
3941
call: CallEntryPoint,

crates/blockifier/src/execution/stack_trace_test.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ Unknown location (pc=0:{expected_pc1})
239239
Error at pc=0:767:
240240
1: Error in the called contract (contract address: {contract_address_felt:#064x}, class hash: \
241241
{test_contract_hash:#064x}, selector: {invoke_call_chain_selector_felt:#064x}):
242-
Error at pc=0:9508:
242+
Error at pc=0:9631:
243243
Cairo traceback (most recent call last):
244244
Unknown location (pc=0:{pc_location})
245245
@@ -259,10 +259,10 @@ Execution failed. Failure reason: {expected_error}.
259259
#[case(CairoVersion::Cairo0, "invoke_call_chain", "Couldn't compute operand op0. Unknown value for memory cell 1:23", 1_u8, 1_u8, (49_u16, 1111_u16, 1081_u16, 1166_u16))]
260260
#[case(CairoVersion::Cairo0, "fail", "An ASSERT_EQ instruction failed: 1 != 0.", 0_u8, 0_u8, (37_u16, 1093_u16, 1184_u16, 1188_u16))]
261261
#[case(CairoVersion::Cairo0, "fail", "An ASSERT_EQ instruction failed: 1 != 0.", 0_u8, 1_u8, (49_u16, 1111_u16, 1184_u16, 1188_u16))]
262-
#[case(CairoVersion::Cairo1, "invoke_call_chain", "0x4469766973696f6e2062792030 ('Division by 0')", 1_u8, 0_u8, (9508_u16, 9508_u16, 0_u16, 0_u16))]
263-
#[case(CairoVersion::Cairo1, "invoke_call_chain", "0x4469766973696f6e2062792030 ('Division by 0')", 1_u8, 1_u8, (9508_u16, 9577_u16, 0_u16, 0_u16))]
264-
#[case(CairoVersion::Cairo1, "fail", "0x6661696c ('fail')", 0_u8, 0_u8, (9508_u16, 9508_u16, 0_u16, 0_u16))]
265-
#[case(CairoVersion::Cairo1, "fail", "0x6661696c ('fail')", 0_u8, 1_u8, (9508_u16, 9577_u16, 0_u16, 0_u16))]
262+
#[case(CairoVersion::Cairo1, "invoke_call_chain", "0x4469766973696f6e2062792030 ('Division by 0')", 1_u8, 0_u8, (9631_u16, 9631_u16, 0_u16, 0_u16))]
263+
#[case(CairoVersion::Cairo1, "invoke_call_chain", "0x4469766973696f6e2062792030 ('Division by 0')", 1_u8, 1_u8, (9631_u16, 9700_u16, 0_u16, 0_u16))]
264+
#[case(CairoVersion::Cairo1, "fail", "0x6661696c ('fail')", 0_u8, 0_u8, (9631_u16, 9631_u16, 0_u16, 0_u16))]
265+
#[case(CairoVersion::Cairo1, "fail", "0x6661696c ('fail')", 0_u8, 1_u8, (9631_u16, 9700_u16, 0_u16, 0_u16))]
266266
fn test_trace_call_chain_with_syscalls(
267267
block_context: BlockContext,
268268
#[case] cairo_version: CairoVersion,

crates/blockifier/src/test_utils.rs

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ pub const TEST_ERC20_CONTRACT_CLASS_HASH: &str = "0x1010";
4747
pub const ERC20_CONTRACT_PATH: &str = "./ERC20/ERC20_Cairo0/ERC20_without_some_syscalls/ERC20/\
4848
erc20_contract_without_some_syscalls_compiled.json";
4949

50+
// TODO(Aviv, 14/7/2024): Move from test utils module, and use it in ContractClassVersionMismatch
51+
// error.
5052
#[derive(Clone, Hash, PartialEq, Eq, Copy, Debug)]
5153
pub enum CairoVersion {
5254
Cairo0,

crates/blockifier/src/transaction.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
pub mod account_transaction;
22
pub mod constants;
3+
#[cfg(test)]
4+
pub mod error_format_test;
35
pub mod errors;
46
pub mod objects;
57
#[cfg(any(feature = "testing", test))]

0 commit comments

Comments
 (0)