diff --git a/Cargo.toml b/Cargo.toml index 00a86de26b1..bb969dd11e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -128,7 +128,7 @@ apollo_reverts.path = "crates/apollo_reverts" apollo_rpc.path = "crates/apollo_rpc" apollo_rpc_execution.path = "crates/apollo_rpc_execution" apollo_starknet_client.path = "crates/apollo_starknet_client" -apollo_starknet_os_program.path = "crates/apollo_starknet_os_program" +apollo_starknet_os_program = { path = "crates/apollo_starknet_os_program", version = "0.15.0-rc.0" } apollo_state_reader.path = "crates/apollo_state_reader" apollo_state_sync.path = "crates/apollo_state_sync" apollo_state_sync_metrics.path = "crates/apollo_state_sync_metrics" diff --git a/crates/apollo_starknet_os_program/build/compile_program.rs b/crates/apollo_starknet_os_program/build/compile_program.rs index 535cb1630d5..6e0b3d07ea2 100644 --- a/crates/apollo_starknet_os_program/build/compile_program.rs +++ b/crates/apollo_starknet_os_program/build/compile_program.rs @@ -3,11 +3,8 @@ use std::path::PathBuf; use apollo_infra_utils::cairo0_compiler::{compile_cairo0_program, Cairo0CompilerError}; use apollo_infra_utils::compile_time_cargo_manifest_dir; -/// Compile the StarkNet OS program. -pub fn compile_starknet_os() -> Vec { - let cairo_root_path = PathBuf::from(compile_time_cargo_manifest_dir!()).join("src/cairo"); - let os_main_path = cairo_root_path.join("starkware/starknet/core/os/os.cairo"); - match compile_cairo0_program(os_main_path, cairo_root_path) { +fn compile_program(path_to_main_file: PathBuf) -> Vec { + match compile_cairo0_program(path_to_main_file, cairo_root_path()) { Ok(bytes) => bytes, Err(Cairo0CompilerError::Cairo0CompilerVersion(error)) => { panic!( @@ -18,7 +15,19 @@ pub fn compile_starknet_os() -> Vec { ) } Err(other_error) => { - panic!("Failed to compile the StarkNet OS program. Error:\n{other_error}.") + panic!("Failed to compile the program. Error:\n{other_error}.") } } } + +fn cairo_root_path() -> PathBuf { + PathBuf::from(compile_time_cargo_manifest_dir!()).join("src/cairo") +} + +pub fn compile_starknet_os() -> Vec { + compile_program(cairo_root_path().join("starkware/starknet/core/os/os.cairo")) +} + +pub fn compile_starknet_aggregator() -> Vec { + compile_program(cairo_root_path().join("starkware/starknet/core/aggregator/main.cairo")) +} diff --git a/crates/apollo_starknet_os_program/build/main.rs b/crates/apollo_starknet_os_program/build/main.rs index 44ae73e5cc7..bee35be5fed 100644 --- a/crates/apollo_starknet_os_program/build/main.rs +++ b/crates/apollo_starknet_os_program/build/main.rs @@ -19,4 +19,11 @@ fn main() { let starknet_os_bytes_path = out_dir.join("starknet_os_bytes"); std::fs::write(&starknet_os_bytes_path, &starknet_os_bytes) .expect("Failed to write the compiled OS bytes to the output directory."); + + println!("cargo::warning=Compiling Starknet aggregator program..."); + let starknet_aggregator_bytes = compile_program::compile_starknet_aggregator(); + println!("cargo::warning=Done. Writing compiled bytes to output directory."); + let starknet_aggregator_bytes_path = out_dir.join("starknet_aggregator_bytes"); + std::fs::write(&starknet_aggregator_bytes_path, &starknet_aggregator_bytes) + .expect("Failed to write the compiled aggregator bytes to the output directory."); } diff --git a/crates/apollo_starknet_os_program/src/lib.rs b/crates/apollo_starknet_os_program/src/lib.rs index bc2ec16ff72..6006ce7f342 100644 --- a/crates/apollo_starknet_os_program/src/lib.rs +++ b/crates/apollo_starknet_os_program/src/lib.rs @@ -4,7 +4,7 @@ use std::sync::LazyLock; use cairo_vm::types::program::Program; -use crate::program_hash::ProgramHash; +use crate::program_hash::ProgramHashes; pub mod program_hash; @@ -15,12 +15,18 @@ pub static CAIRO_FILES_MAP: LazyLock> = LazyLock::new(|| }); pub const OS_PROGRAM_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/starknet_os_bytes")); +pub const AGGREGATOR_PROGRAM_BYTES: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/starknet_aggregator_bytes")); pub static OS_PROGRAM: LazyLock = LazyLock::new(|| { Program::from_bytes(OS_PROGRAM_BYTES, Some("main")).expect("Failed to load the OS bytes.") }); +pub static AGGREGATOR_PROGRAM: LazyLock = LazyLock::new(|| { + Program::from_bytes(AGGREGATOR_PROGRAM_BYTES, Some("main")) + .expect("Failed to load the aggregator bytes.") +}); -pub static PROGRAM_HASH: LazyLock = LazyLock::new(|| { +pub static PROGRAM_HASHES: LazyLock = LazyLock::new(|| { // As the program hash file may not exist at runtime, it's contents must be included at compile // time. serde_json::from_str(include_str!("program_hash.json")) diff --git a/crates/apollo_starknet_os_program/src/program_hash.json b/crates/apollo_starknet_os_program/src/program_hash.json index 94c57272f0f..2ea0dbd730e 100644 --- a/crates/apollo_starknet_os_program/src/program_hash.json +++ b/crates/apollo_starknet_os_program/src/program_hash.json @@ -1,3 +1,5 @@ { - "os": "0x1a07803807a087714c365cc2babcf19cf5ebc4d939cccc5df1e0019554c560d" -} \ No newline at end of file + "os": "0x1a07803807a087714c365cc2babcf19cf5ebc4d939cccc5df1e0019554c560d", + "aggregator": "0x181986bfe23bbfdac56c40522bd5c2b1b64c824c74bd6722a3ccacc8cc888e2", + "aggregator_with_prefix": "0x1ae51bd7157e0754f1e1729806ad5579a7ac6926cd0c8dd342ad1e7a3758c2e" +} diff --git a/crates/apollo_starknet_os_program/src/program_hash.rs b/crates/apollo_starknet_os_program/src/program_hash.rs index d0183047a13..b5240e0244f 100644 --- a/crates/apollo_starknet_os_program/src/program_hash.rs +++ b/crates/apollo_starknet_os_program/src/program_hash.rs @@ -1,10 +1,11 @@ use cairo_vm::types::builtin_name::BuiltinName; use cairo_vm::types::errors::program_errors::ProgramError; +use cairo_vm::types::program::Program; use serde::{Deserialize, Serialize}; use starknet_types_core::felt::Felt; use starknet_types_core::hash::{Pedersen, StarkHash}; -use crate::OS_PROGRAM; +use crate::{AGGREGATOR_PROGRAM, OS_PROGRAM}; #[cfg(test)] #[path = "program_hash_test.rs"] @@ -21,12 +22,29 @@ pub enum ProgramHashError { } #[derive(Debug, Deserialize, Serialize, PartialEq)] -pub struct ProgramHash { +pub struct ProgramHashes { pub os: Felt, + pub aggregator: Felt, + pub aggregator_with_prefix: Felt, +} + +pub struct AggregatorHash { + pub with_prefix: Felt, + pub without_prefix: Felt, } const BOOTLOADER_VERSION: u8 = 0; +fn pad_to_32_bytes(data: &[u8]) -> [u8; 32] { + let mut padded = [0; 32]; + let len = data.len(); + if len > 32 { + panic!("Data length exceeds 32 bytes."); + } + padded[32 - len..].copy_from_slice(data); + padded +} + fn pedersen_hash_chain(data: Vec) -> Felt { let length = Felt::from(data.len()); vec![length] @@ -37,8 +55,8 @@ fn pedersen_hash_chain(data: Vec) -> Felt { .expect("Hash data chain is not empty.") } -pub fn compute_os_program_hash() -> Result { - let builtins = OS_PROGRAM +fn compute_program_hash(program: &Program) -> Result { + let builtins = program .iter_builtins() .map(|builtin| { let builtin_bytes = builtin.to_str().to_string().into_bytes(); @@ -48,14 +66,7 @@ pub fn compute_os_program_hash() -> Result { name: builtin.to_str().to_string(), }) } else { - let mut padded_builtin_bytes = [0].repeat(32 - builtin_bytes.len()); - padded_builtin_bytes.extend(builtin_bytes); - Ok(Felt::from_bytes_be( - padded_builtin_bytes - .as_slice() - .try_into() - .expect("Padded bytes are 32 bytes long."), - )) + Ok(Felt::from_bytes_be(&pad_to_32_bytes(&builtin_bytes))) } }) .collect::, _>>()?; @@ -63,10 +74,10 @@ pub fn compute_os_program_hash() -> Result { Felt::from(BOOTLOADER_VERSION), // TODO(Dori): When [available](https://github.com/lambdaclass/cairo-vm/pull/2101), use the // Program::get_main() getter instead of the get_stripped_program() method. - Felt::from(OS_PROGRAM.get_stripped_program()?.main), + Felt::from(program.get_stripped_program()?.main), Felt::from(builtins.len()), ]; - let data = OS_PROGRAM + let data = program .iter_data() .map(|data| data.get_int().ok_or(ProgramHashError::UnexpectedRelocatable)) .collect::, _>>()?; @@ -74,3 +85,15 @@ pub fn compute_os_program_hash() -> Result { let data_chain: Vec = program_header.into_iter().chain(builtins).chain(data).collect(); Ok(pedersen_hash_chain(data_chain)) } + +pub fn compute_os_program_hash() -> Result { + compute_program_hash(&OS_PROGRAM) +} + +pub fn compute_aggregator_program_hash() -> Result { + let hash = compute_program_hash(&AGGREGATOR_PROGRAM)?; + Ok(AggregatorHash { + with_prefix: Pedersen::hash(&Felt::from_bytes_be(&pad_to_32_bytes(b"AGGREGATOR")), &hash), + without_prefix: hash, + }) +} diff --git a/crates/apollo_starknet_os_program/src/program_hash_test.rs b/crates/apollo_starknet_os_program/src/program_hash_test.rs index e29c7bdb13d..a9609f09a3c 100644 --- a/crates/apollo_starknet_os_program/src/program_hash_test.rs +++ b/crates/apollo_starknet_os_program/src/program_hash_test.rs @@ -3,8 +3,13 @@ use std::sync::LazyLock; use apollo_infra_utils::compile_time_cargo_manifest_dir; -use crate::program_hash::{compute_os_program_hash, ProgramHash}; -use crate::PROGRAM_HASH; +use crate::program_hash::{ + compute_aggregator_program_hash, + compute_os_program_hash, + AggregatorHash, + ProgramHashes, +}; +use crate::PROGRAM_HASHES; static PROGRAM_HASH_PATH: LazyLock = LazyLock::new(|| { PathBuf::from(compile_time_cargo_manifest_dir!()).join("src/program_hash.json") @@ -14,18 +19,23 @@ static PROGRAM_HASH_PATH: LazyLock = LazyLock::new(|| { /// JSON. /// To fix this test, run the following command: /// ```bash -/// FIX_PROGRAM_HASH=1 cargo test -p apollo_starknet_os_program test_program_hash +/// FIX_PROGRAM_HASH=1 cargo test -p apollo_starknet_os_program test_program_hashes /// ``` #[test] -fn test_program_hash() { - let computed_hash = ProgramHash { os: compute_os_program_hash().unwrap() }; +fn test_program_hashes() { + let AggregatorHash { with_prefix, without_prefix } = compute_aggregator_program_hash().unwrap(); + let computed_hashes = ProgramHashes { + os: compute_os_program_hash().unwrap(), + aggregator: without_prefix, + aggregator_with_prefix: with_prefix, + }; if std::env::var("FIX_PROGRAM_HASH").is_ok() { std::fs::write( PROGRAM_HASH_PATH.as_path(), - serde_json::to_string_pretty(&computed_hash).unwrap(), + serde_json::to_string_pretty(&computed_hashes).unwrap(), ) .unwrap_or_else(|error| panic!("Failed to write the program hash file: {error:?}.")); } else { - assert_eq!(computed_hash, *PROGRAM_HASH); + assert_eq!(computed_hashes, *PROGRAM_HASHES); } } diff --git a/crates/starknet_committer_and_os_cli/src/os_cli/commands.rs b/crates/starknet_committer_and_os_cli/src/os_cli/commands.rs index 3dbb91288a7..9233b8aa907 100644 --- a/crates/starknet_committer_and_os_cli/src/os_cli/commands.rs +++ b/crates/starknet_committer_and_os_cli/src/os_cli/commands.rs @@ -1,7 +1,7 @@ use std::fs; use std::path::Path; -use apollo_starknet_os_program::{CAIRO_FILES_MAP, OS_PROGRAM_BYTES, PROGRAM_HASH}; +use apollo_starknet_os_program::{CAIRO_FILES_MAP, OS_PROGRAM_BYTES, PROGRAM_HASHES}; use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; use cairo_vm::types::layout_name::LayoutName; use cairo_vm::vm::runners::cairo_pie::CairoPie; @@ -119,5 +119,5 @@ pub(crate) fn dump_program(output_path: String, program: ProgramToDump) { } pub(crate) fn dump_program_hashes(output_path: String) { - write_to_file(&output_path, &*PROGRAM_HASH); + write_to_file(&output_path, &*PROGRAM_HASHES); } diff --git a/workspace_tests/version_integrity_test.rs b/workspace_tests/version_integrity_test.rs index 2e284782b83..2fc5ba6948e 100644 --- a/workspace_tests/version_integrity_test.rs +++ b/workspace_tests/version_integrity_test.rs @@ -11,11 +11,11 @@ use toml_test_utils::{ }; const PARENT_BRANCH: &str = include_str!("../scripts/parent_branch.txt"); -const MAIN_PARENT_BRANCH: &str = "main"; +const MAIN_BRANCH: &str = "main"; const EXPECTED_MAIN_VERSION: &str = "0.0.0"; static ROOT_CRATES_FOR_PUBLISH: LazyLock> = - LazyLock::new(|| HashSet::from(["blockifier"])); + LazyLock::new(|| HashSet::from(["blockifier", "apollo_starknet_os_program"])); static CRATES_FOR_PUBLISH: LazyLock> = LazyLock::new(|| { let publish_deps: HashSet = ROOT_CRATES_FOR_PUBLISH .iter() @@ -224,12 +224,12 @@ fn test_no_features_in_workspace() { #[test] fn test_main_branch_is_versionless() { - if PARENT_BRANCH.trim() == MAIN_PARENT_BRANCH { + if PARENT_BRANCH.trim() == MAIN_BRANCH { let workspace_version = ROOT_TOML.workspace_version(); assert_eq!( workspace_version, EXPECTED_MAIN_VERSION, "The workspace version should be '{EXPECTED_MAIN_VERSION}' when the parent branch is \ - '{MAIN_PARENT_BRANCH}'; found {workspace_version}.", + '{MAIN_BRANCH}'; found {workspace_version}.", ); } }