Skip to content

Commit

Permalink
Feature: Add support for SHA256_PROCESS_BLOCK syscall (#348)
Browse files Browse the repository at this point in the history
* feat: add Sha256ProcessBlock syscall

* fmt + clippy

* Write response to out_state properly

* implement exit_sha256_process_block_syscall

* hint_1: use cairo-vm implementation

* update cairo-vm commit

* address review comments. add integration test for failing block

---------

Co-authored-by: Stephen Shelton <[email protected]>
Co-authored-by: ftheirs <[email protected]>
  • Loading branch information
3 people authored Sep 13, 2024
1 parent 7cde766 commit dd0af9e
Show file tree
Hide file tree
Showing 12 changed files with 147 additions and 26 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions crates/bin/prove_block/tests/prove_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)]
Expand Down
1 change: 1 addition & 0 deletions crates/starknet-os/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
4 changes: 4 additions & 0 deletions crates/starknet-os/src/execution/constants.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 =
Expand Down
4 changes: 4 additions & 0 deletions crates/starknet-os/src/execution/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ where
// Secp syscall processors.
pub secp256k1_syscall_processor: SecpSyscallProcessor<ark_secp256k1::Config>,
pub secp256r1_syscall_processor: SecpSyscallProcessor<ark_secp256r1::Config>,

// Sha256 segments
pub sha256_segment: Option<Relocatable>,
}
/// ExecutionHelper is wrapped in Rc<RefCell<_>> in order
/// to clone the refrence when entering and exiting vm scopes
Expand Down Expand Up @@ -137,6 +140,7 @@ where
storage_by_address: contract_storage_map,
secp256k1_syscall_processor: Default::default(),
secp256r1_syscall_processor: Default::default(),
sha256_segment: None,
})),
}
}
Expand Down
86 changes: 81 additions & 5 deletions crates/starknet-os/src/execution/syscall_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -37,7 +38,6 @@ where
pub exec_wrapper: ExecutionHelperWrapper<PCS>,
pub syscall_ptr: Option<Relocatable>,
pub segments: ReadOnlySegments,
pub sha256_segment: Option<Relocatable>,
}

/// OsSyscallHandler is wrapped in Rc<RefCell<_>> in order
Expand Down Expand Up @@ -69,7 +69,6 @@ where
exec_wrapper,
syscall_ptr: None,
segments: ReadOnlySegments::default(),
sha256_segment: None,
})),
}
}
Expand All @@ -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> {
Expand Down Expand Up @@ -182,6 +182,9 @@ where
SyscallSelector::Secp256r1Add => {
run_handler::<SecpAddHandler<ark_secp256r1::Config>, PCS>(ptr, vm, ehw, SECP256R1_ADD_GAS_COST).await
}
SyscallSelector::Sha256ProcessBlock => {
run_handler::<Sha256ProcessBlockHandler, PCS>(ptr, vm, ehw, SHA256_PROCESS_BLOCK_GAS_COST).await
}

_ => Err(HintError::CustomHint(format!("Unknown syscall selector: {:?}", selector).into())),
}?;
Expand Down Expand Up @@ -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<PCS> SyscallHandler<PCS> for Sha256ProcessBlockHandler
where
PCS: PerContractStorage + 'static,
{
type Request = Sha256ProcessBlockRequest;
type Response = Sha256ProcessBlockResponse;

fn read_request(vm: &VirtualMachine, ptr: &mut Relocatable) -> SyscallResult<Self::Request> {
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<PCS>,
_remaining_gas: &mut u64,
) -> SyscallResult<Self::Response> {
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<u32>.")
});

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<MaybeRelocatable> =
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 })
}
}
2 changes: 2 additions & 0 deletions crates/starknet-os/src/execution/syscall_handler_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub enum SyscallSelector {
LibraryCall,
LibraryCallL1Handler,
ReplaceClass,
Sha256ProcessBlock,
Secp256k1Add,
Secp256k1GetPointFromX,
Secp256k1GetXy,
Expand Down Expand Up @@ -77,6 +78,7 @@ impl TryFrom<Felt252> 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),
Expand Down
33 changes: 33 additions & 0 deletions crates/starknet-os/src/hints/execute_transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<String, HintReference>,
ap_tracking: &ApTracking,
_constants: &HashMap<String, Felt252>,
) -> 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(())
}
4 changes: 3 additions & 1 deletion crates/starknet-os/src/hints/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ fn hints<PCS>() -> HashMap<String, HintImpl> 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);
Expand Down Expand Up @@ -248,6 +249,7 @@ fn hints<PCS>() -> HashMap<String, HintImpl> 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
}

Expand Down
11 changes: 11 additions & 0 deletions crates/starknet-os/src/hints/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, HintReference>,
ap_tracking: &ApTracking,
constants: &HashMap<String, Felt252>,
) -> 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(
Expand Down
19 changes: 0 additions & 19 deletions crates/starknet-os/src/hints/unimplemented.rs
Original file line number Diff line number Diff line change
@@ -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)"#};
4 changes: 4 additions & 0 deletions crates/starknet-os/src/hints/vars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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 {
Expand Down

0 comments on commit dd0af9e

Please sign in to comment.