diff --git a/Cargo.toml b/Cargo.toml index c51a5162f..4d8d03718 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ cairo-lang-starknet-classes = { version = "=2.7.1" } cairo-lang-utils = { version = "=2.7.1" } cairo-lang-casm = { version = "=2.7.1" } cairo-type-derive = { version = "0.1.0", path = "crates/cairo-type-derive" } -cairo-vm = { git = "https://github.com/Moonsong-Labs/cairo-vm", rev = "40ebe36e64ffa429ff18cba8ef0bd7d2c122ac12", features = ["cairo-1-hints", "extensive_hints", "mod_builtin"] } +cairo-vm = { git = "https://github.com/Moonsong-Labs/cairo-vm", rev = "a581ba5aea56489d0ad3127e71bc666615ce2224", features = ["cairo-1-hints", "extensive_hints", "mod_builtin"] } clap = { version = "4.5.4", features = ["derive"] } c-kzg = { version = "1.0.3" } env_logger = "0.11.3" @@ -64,6 +64,7 @@ serde = { version = "1.0.188", features = ["derive"] } serde_json = { version = "1.0.105", features = ["arbitrary_precision"] } serde_with = "3.3.0" serde_yaml = "0.9.25" +sha2 = "0.10.8" starknet = "0.11.0" starknet_api = { git = "https://github.com/Moonsong-Labs/sequencer", rev = "6624e910c57db9a16f1607c1ed26f7d8f1114e73", features = ["testing"] } starknet-core = "0.11.1" diff --git a/crates/bin/prove_block/tests/prove_block.rs b/crates/bin/prove_block/tests/prove_block.rs index ab6aea1c1..213294d30 100644 --- a/crates/bin/prove_block/tests/prove_block.rs +++ b/crates/bin/prove_block/tests/prove_block.rs @@ -7,6 +7,7 @@ use rstest::rstest; // # * 76766 / 76775: additional basic blocks // # * 76832: contains a reverted tx // # * 86507 / 124533: a failing assert that happened because we used the wrong VersionedConstants +// # * 87023: failing for Core(TestLessThanOrEqualAddress) hint not being implemented in cairo vm and SHA256_PROCESS_BLOCK syscall in SNOS // # * 87019: diff assert values in contract subcall // # * 90000: one of the subcalls results in a call to `replace_class()`. // # * 87041: block with nonce bump inconsistency @@ -18,6 +19,7 @@ use rstest::rstest; #[case::additional_basic_blocks_2(76775)] #[case::block_with_reverted_tx(76832)] #[case::failing_assert_on_versioned_constants_1(86507)] +#[case::core_hint_test_less_than_or_equal_address(87023)] #[case::failing_assert_on_versioned_constants_2(124533)] #[case::fix_diff_assert_values_in_contract_subcall(87019)] #[case::invoke_with_replace_class(90000)] diff --git a/crates/starknet-os/Cargo.toml b/crates/starknet-os/Cargo.toml index e972d698e..cf473ef5b 100644 --- a/crates/starknet-os/Cargo.toml +++ b/crates/starknet-os/Cargo.toml @@ -40,6 +40,7 @@ serde = { workspace = true } serde_json = { workspace = true } serde_with = { workspace = true } serde_yaml = { workspace = true } +sha2 = { workspace = true } starknet_api = { workspace = true } starknet-crypto = { workspace = true } starknet-os-types = { path = "../starknet-os-types" } diff --git a/crates/starknet-os/src/execution/constants.rs b/crates/starknet-os/src/execution/constants.rs index b4f81dad0..ce5f5cd2d 100644 --- a/crates/starknet-os/src/execution/constants.rs +++ b/crates/starknet-os/src/execution/constants.rs @@ -1,6 +1,7 @@ // Gas Cost. // See documentation in core/os/constants.cairo. pub const BLOCK_HASH_CONTRACT_ADDRESS: u64 = 1; +pub const BITWISE_BUILTIN_GAS_COST: u64 = 594; pub const STEP_GAS_COST: u64 = 100; pub const RANGE_CHECK_GAS_COST: u64 = 70; @@ -39,6 +40,9 @@ pub const LIBRARY_CALL_GAS_COST: u64 = CALL_CONTRACT_GAS_COST; #[allow(unused)] pub const REPLACE_CLASS_GAS_COST: u64 = SYSCALL_BASE_GAS_COST + 50 * STEP_GAS_COST; #[allow(unused)] +pub const SHA256_PROCESS_BLOCK_GAS_COST: u64 = + 1115 * BITWISE_BUILTIN_GAS_COST + 65 * RANGE_CHECK_GAS_COST + 1852 * STEP_GAS_COST + SYSCALL_BASE_GAS_COST; +#[allow(unused)] pub const SECP256K1_ADD_GAS_COST: u64 = 406 * STEP_GAS_COST + 29 * RANGE_CHECK_GAS_COST; #[allow(unused)] pub const SECP256K1_GET_POINT_FROM_X_GAS_COST: u64 = diff --git a/crates/starknet-os/src/execution/helper.rs b/crates/starknet-os/src/execution/helper.rs index d18e20ff2..cf04f25ee 100644 --- a/crates/starknet-os/src/execution/helper.rs +++ b/crates/starknet-os/src/execution/helper.rs @@ -59,6 +59,9 @@ where // Secp syscall processors. pub secp256k1_syscall_processor: SecpSyscallProcessor, pub secp256r1_syscall_processor: SecpSyscallProcessor, + + // Sha256 segments + pub sha256_segment: Option, } /// ExecutionHelper is wrapped in Rc> in order /// to clone the refrence when entering and exiting vm scopes @@ -137,6 +140,7 @@ where storage_by_address: contract_storage_map, secp256k1_syscall_processor: Default::default(), secp256r1_syscall_processor: Default::default(), + sha256_segment: None, })), } } diff --git a/crates/starknet-os/src/execution/syscall_handler.rs b/crates/starknet-os/src/execution/syscall_handler.rs index a5f551870..a23f3ddd3 100644 --- a/crates/starknet-os/src/execution/syscall_handler.rs +++ b/crates/starknet-os/src/execution/syscall_handler.rs @@ -17,7 +17,8 @@ use crate::execution::constants::{ KECCAK_ROUND_COST_GAS_COST, LIBRARY_CALL_GAS_COST, REPLACE_CLASS_GAS_COST, SECP256K1_ADD_GAS_COST, SECP256K1_GET_POINT_FROM_X_GAS_COST, SECP256K1_GET_XY_GAS_COST, SECP256K1_MUL_GAS_COST, SECP256K1_NEW_GAS_COST, SECP256R1_ADD_GAS_COST, SECP256R1_GET_POINT_FROM_X_GAS_COST, SECP256R1_GET_XY_GAS_COST, SECP256R1_MUL_GAS_COST, - SECP256R1_NEW_GAS_COST, SEND_MESSAGE_TO_L1_GAS_COST, STORAGE_READ_GAS_COST, STORAGE_WRITE_GAS_COST, + SECP256R1_NEW_GAS_COST, SEND_MESSAGE_TO_L1_GAS_COST, SHA256_PROCESS_BLOCK_GAS_COST, STORAGE_READ_GAS_COST, + STORAGE_WRITE_GAS_COST, }; use crate::execution::secp_handler::{ SecpAddHandler, SecpGetPointFromXHandler, SecpGetXyHandler, SecpMulHandler, SecpNewHandler, @@ -37,7 +38,6 @@ where pub exec_wrapper: ExecutionHelperWrapper, pub syscall_ptr: Option, pub segments: ReadOnlySegments, - pub sha256_segment: Option, } /// OsSyscallHandler is wrapped in Rc> in order @@ -69,7 +69,6 @@ where exec_wrapper, syscall_ptr: None, segments: ReadOnlySegments::default(), - sha256_segment: None, })), } } @@ -84,8 +83,9 @@ where } pub async fn set_sha256_segment(&self, sha256_segment: Relocatable) { - let mut syscall_handler = self.syscall_handler.write().await; - syscall_handler.sha256_segment = Some(sha256_segment); + let syscall_handler = self.syscall_handler.write().await; + let mut execution_helper = syscall_handler.exec_wrapper.execution_helper.write().await; + execution_helper.sha256_segment = Some(sha256_segment); } pub async fn validate_and_discard_syscall_ptr(&self, syscall_ptr_end: Relocatable) -> Result<(), HintError> { @@ -182,6 +182,9 @@ where SyscallSelector::Secp256r1Add => { run_handler::, PCS>(ptr, vm, ehw, SECP256R1_ADD_GAS_COST).await } + SyscallSelector::Sha256ProcessBlock => { + run_handler::(ptr, vm, ehw, SHA256_PROCESS_BLOCK_GAS_COST).await + } _ => Err(HintError::CustomHint(format!("Unknown syscall selector: {:?}", selector).into())), }?; @@ -671,3 +674,76 @@ where Ok(()) } } + +pub struct Sha256ProcessBlockHandler; + +#[derive(Debug, Eq, PartialEq)] +pub struct Sha256ProcessBlockRequest { + pub state_ptr: Relocatable, + pub input_start: Relocatable, +} + +#[derive(Debug, Eq, PartialEq)] +pub struct Sha256ProcessBlockResponse { + pub state_ptr: Relocatable, +} + +impl SyscallHandler for Sha256ProcessBlockHandler +where + PCS: PerContractStorage + 'static, +{ + type Request = Sha256ProcessBlockRequest; + type Response = Sha256ProcessBlockResponse; + + fn read_request(vm: &VirtualMachine, ptr: &mut Relocatable) -> SyscallResult { + let state_ptr = vm.get_relocatable(*ptr)?; + *ptr = (*ptr + 1)?; + let input_start = vm.get_relocatable(*ptr)?; + *ptr = (*ptr + 1)?; + Ok(Sha256ProcessBlockRequest { state_ptr, input_start }) + } + + fn write_response(response: Self::Response, vm: &mut VirtualMachine, ptr: &mut Relocatable) -> WriteResponseResult { + write_maybe_relocatable(vm, ptr, response.state_ptr)?; + Ok(()) + } + + async fn execute( + request: Self::Request, + vm: &mut VirtualMachine, + exec_wrapper: &mut ExecutionHelperWrapper, + _remaining_gas: &mut u64, + ) -> SyscallResult { + let mut eh_ref = exec_wrapper.execution_helper.write().await; + const SHA256_BLOCK_SIZE: usize = 16; + + let data = vm.get_integer_range(request.input_start, SHA256_BLOCK_SIZE)?; + const SHA256_STATE_SIZE: usize = 8; + let prev_state = vm.get_integer_range(request.state_ptr, SHA256_STATE_SIZE)?; + + let data_as_bytes = sha2::digest::generic_array::GenericArray::from_exact_iter(data.iter().flat_map(|felt| { + felt.to_bigint().to_u32().expect("libfunc should ensure the input is an [u32; 16].").to_be_bytes() + })) + .expect("u32.to_be_bytes() returns 4 bytes, and data.len() == 16. So data contains 64 bytes."); + + let mut state_as_words: [u32; SHA256_STATE_SIZE] = core::array::from_fn(|i| { + prev_state[i] + .to_bigint() + .to_u32() + .expect("libfunc only accepts SHA256StateHandle which can only be created from an Array.") + }); + + sha2::compress256(&mut state_as_words, &[data_as_bytes]); + + let segment = eh_ref.sha256_segment.unwrap_or(vm.add_memory_segment()); + + let response = (segment + 24)?; // 'out_state' is 24 bytes into 'struct Sha256ProcessBlock' + let data: Vec = + state_as_words.iter().map(|&arg| MaybeRelocatable::from(Felt252::from(arg))).collect(); + + // side effect: this advances sha256_segment by 32 bytes (the size of struct Sha256ProcessBlock) + eh_ref.sha256_segment = Some(vm.load_data(response, &data)?); + + Ok(Sha256ProcessBlockResponse { state_ptr: response }) + } +} diff --git a/crates/starknet-os/src/execution/syscall_handler_utils.rs b/crates/starknet-os/src/execution/syscall_handler_utils.rs index da5e2c5ab..54aa2a767 100644 --- a/crates/starknet-os/src/execution/syscall_handler_utils.rs +++ b/crates/starknet-os/src/execution/syscall_handler_utils.rs @@ -36,6 +36,7 @@ pub enum SyscallSelector { LibraryCall, LibraryCallL1Handler, ReplaceClass, + Sha256ProcessBlock, Secp256k1Add, Secp256k1GetPointFromX, Secp256k1GetXy, @@ -77,6 +78,7 @@ impl TryFrom for SyscallSelector { b"LibraryCall" => Ok(Self::LibraryCall), b"LibraryCallL1Handler" => Ok(Self::LibraryCallL1Handler), b"ReplaceClass" => Ok(Self::ReplaceClass), + b"Sha256ProcessBlock" => Ok(Self::Sha256ProcessBlock), b"Secp256k1Add" => Ok(Self::Secp256k1Add), b"Secp256k1GetPointFromX" => Ok(Self::Secp256k1GetPointFromX), b"Secp256k1GetXy" => Ok(Self::Secp256k1GetXy), diff --git a/crates/starknet-os/src/hints/execute_transactions.rs b/crates/starknet-os/src/hints/execute_transactions.rs index f08cc581a..c4a4fc3b7 100644 --- a/crates/starknet-os/src/hints/execute_transactions.rs +++ b/crates/starknet-os/src/hints/execute_transactions.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use cairo_vm::hint_processor::builtin_hint_processor::hint_utils::{ get_integer_from_var_name, get_ptr_from_var_name, get_relocatable_from_var_name, }; +use cairo_vm::hint_processor::builtin_hint_processor::sha256_utils::sha256_finalize; use cairo_vm::hint_processor::hint_processor_definition::HintReference; use cairo_vm::serde::deserialize_program::ApTracking; use cairo_vm::types::exec_scope::ExecutionScopes; @@ -133,3 +134,35 @@ pub fn fill_holes_in_rc96_segment( Ok(()) } + +pub const SHA2_FINALIZE: &str = indoc! {r#"# Add dummy pairs of input and output. +from starkware.cairo.common.cairo_sha256.sha256_utils import ( + IV, + compute_message_schedule, + sha2_compress_function, +) + +number_of_missing_blocks = (-ids.n) % ids.BATCH_SIZE +assert 0 <= number_of_missing_blocks < 20 +_sha256_input_chunk_size_felts = ids.SHA256_INPUT_CHUNK_SIZE_FELTS +assert 0 <= _sha256_input_chunk_size_felts < 100 + +message = [0] * _sha256_input_chunk_size_felts +w = compute_message_schedule(message) +output = sha2_compress_function(IV, w) +padding = (message + IV + output) * number_of_missing_blocks +segments.write_arg(ids.sha256_ptr_end, padding)"#}; + +pub fn sha2_finalize( + vm: &mut VirtualMachine, + _exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + // All the logic that is needed here is already implemented in cairo-vm + // Check sha256_utils.rs for implementation details + sha256_finalize(vm, ids_data, ap_tracking)?; + + Ok(()) +} diff --git a/crates/starknet-os/src/hints/mod.rs b/crates/starknet-os/src/hints/mod.rs index e0e2deb79..7f69409ad 100644 --- a/crates/starknet-os/src/hints/mod.rs +++ b/crates/starknet-os/src/hints/mod.rs @@ -215,7 +215,8 @@ fn hints() -> HashMap where hints.insert(syscalls::EXIT_LIBRARY_CALL_L1_HANDLER_SYSCALL.into(), syscalls::exit_library_call_l1_handler_syscall); hints.insert(syscalls::EXIT_LIBRARY_CALL_SYSCALL.into(), syscalls::exit_library_call_syscall); hints.insert(syscalls::EXIT_REPLACE_CLASS_SYSCALL.into(), syscalls::exit_replace_class_syscall); - hints.insert(syscalls::EXIT_SECP256K1_ADD_SYSCALL.into(), syscalls::exit_secp256k1_add_syscall); + hints.insert(syscalls::EXIT_SECP256K1_ADD_SYSCALL.into(), syscalls::exit_sha256_process_block_syscall); + hints.insert(syscalls::EXIT_SHA256_PROCESS_BLOCK_SYSCALL.into(), syscalls::exit_secp256k1_add_syscall); hints.insert(syscalls::EXIT_SECP256K1_GET_POINT_FROM_X_SYSCALL.into(), syscalls::exit_secp256k1_get_point_from_x_syscall); hints.insert(syscalls::EXIT_SECP256K1_GET_XY_SYSCALL.into(), syscalls::exit_secp256k1_get_xy_syscall); hints.insert(syscalls::EXIT_SECP256K1_MUL_SYSCALL.into(), syscalls::exit_secp256k1_mul_syscall); @@ -248,6 +249,7 @@ fn hints() -> HashMap where hints.insert(block_context::WRITE_USE_KZG_DA_TO_MEM.into(), block_context::write_use_kzg_da_to_mem); hints.insert(compiled_class::SET_AP_TO_SEGMENT_HASH.into(), compiled_class::set_ap_to_segment_hash); hints.insert(secp::READ_EC_POINT_ADDRESS.into(), secp::read_ec_point_from_address); + hints.insert(execute_transactions::SHA2_FINALIZE.into(), execute_transactions::sha2_finalize); hints } diff --git a/crates/starknet-os/src/hints/syscalls.rs b/crates/starknet-os/src/hints/syscalls.rs index 28ea03af6..4c02113d7 100644 --- a/crates/starknet-os/src/hints/syscalls.rs +++ b/crates/starknet-os/src/hints/syscalls.rs @@ -682,6 +682,17 @@ pub fn exit_replace_class_syscall( ) -> Result<(), HintError> { exit_syscall("REPLACE_CLASS_SELECTOR", vm, exec_scopes, ids_data, ap_tracking, constants) } +pub const EXIT_SHA256_PROCESS_BLOCK_SYSCALL: &str = "exit_syscall(selector=ids.SHA256_PROCESS_BLOCK_SELECTOR)"; + +pub fn exit_sha256_process_block_syscall( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + constants: &HashMap, +) -> Result<(), HintError> { + exit_syscall("SHA256_PROCESS_BLOCK_SELECTOR", vm, exec_scopes, ids_data, ap_tracking, constants) +} pub const EXIT_SECP256K1_ADD_SYSCALL: &str = "exit_syscall(selector=ids.SECP256K1_ADD_SELECTOR)"; pub fn exit_secp256k1_add_syscall( diff --git a/crates/starknet-os/src/hints/unimplemented.rs b/crates/starknet-os/src/hints/unimplemented.rs index 1d15a5e99..a55861d52 100644 --- a/crates/starknet-os/src/hints/unimplemented.rs +++ b/crates/starknet-os/src/hints/unimplemented.rs @@ -1,23 +1,4 @@ use indoc::indoc; -#[allow(unused)] -pub const HINT_1: &str = indoc! {r#"# Add dummy pairs of input and output. -from starkware.cairo.common.cairo_sha256.sha256_utils import ( - IV, - compute_message_schedule, - sha2_compress_function, -) - -number_of_missing_blocks = (-ids.n) % ids.BATCH_SIZE -assert 0 <= number_of_missing_blocks < 20 -_sha256_input_chunk_size_felts = ids.SHA256_INPUT_CHUNK_SIZE_FELTS -assert 0 <= _sha256_input_chunk_size_felts < 100 - -message = [0] * _sha256_input_chunk_size_felts -w = compute_message_schedule(message) -output = sha2_compress_function(IV, w) -padding = (message + IV + output) * number_of_missing_blocks -segments.write_arg(ids.sha256_ptr_end, padding)"#}; - #[allow(unused)] pub const HINT_4: &str = indoc! {r#"exit_syscall(selector=ids.SHA256_PROCESS_BLOCK_SELECTOR)"#}; diff --git a/crates/starknet-os/src/hints/vars.rs b/crates/starknet-os/src/hints/vars.rs index 1904d4c13..6653ed7d4 100644 --- a/crates/starknet-os/src/hints/vars.rs +++ b/crates/starknet-os/src/hints/vars.rs @@ -130,6 +130,7 @@ pub mod ids { pub const SELECTOR: &str = "selector"; pub const SENDER_ADDRESS: &str = "sender_address"; pub const SHA256_PTR: &str = "sha256_ptr"; + pub const SHA256_PTR_END: &str = "sha256_ptr_end"; pub const SIBLINGS: &str = "siblings"; pub const SIGNATURE_LEN: &str = "signature_len"; pub const SIGNATURE_START: &str = "signature_start"; @@ -149,6 +150,9 @@ pub mod ids { pub const WORD: &str = "word"; pub const Y: &str = "y"; pub const Y_SQUARE_INT: &str = "y_square_int"; + pub const BATCH_SIZE: &str = "starkware.cairo.common.cairo_sha256.sha256_utils.BATCH_SIZE"; + pub const SHA256_INPUT_CHUNK_SIZE_FELTS: &str = + "starkware.cairo.common.cairo_sha256.sha256_utils.SHA256_INPUT_CHUNK_SIZE_FELTS"; } pub mod constants {