diff --git a/.github/workflows/rust-vm.yaml b/.github/workflows/rust-vm.yaml index 5ab2ca0e..7a7b3a29 100644 --- a/.github/workflows/rust-vm.yaml +++ b/.github/workflows/rust-vm.yaml @@ -103,8 +103,9 @@ jobs: - name: Run Nextest Tests env: RPC_URL_ETHEREUM: ${{ secrets.RPC_URL_ETHEREUM_SEPOLIA }} - RPC_URL_FEEDER_GATEWAY: ${{ secrets.RPC_URL_FEEDER_GATEWAY }} - RPC_URL_HERODOTUS_INDEXER: ${{ secrets.RPC_URL_HERODOTUS_INDEXER }} + RPC_URL_HERODOTUS_INDEXER_GROWER: ${{ secrets.RPC_URL_HERODOTUS_INDEXER_GROWER }} + RPC_URL_HERODOTUS_INDEXER_STAGING: ${{ secrets.RPC_URL_HERODOTUS_INDEXER_STAGING }} RPC_URL_STARKNET: ${{ secrets.RPC_URL_STARKNET_SEPOLIA }} + HERODOTUS_STAGING_INDEXER: ${{ secrets.HERODOTUS_STAGING_INDEXER }} run: cargo nextest run diff --git a/Cargo.lock b/Cargo.lock index e4acb857..886770ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2499,7 +2499,7 @@ dependencies = [ "cairo-lang-starknet-classes", "cairo-vm", "hints", - "reqwest 0.12.12", + "indexer", "serde", "starknet", "starknet-types-core", @@ -3621,6 +3621,7 @@ name = "indexer" version = "0.1.0" dependencies = [ "alloy 0.7.3", + "cairo-vm", "reqwest 0.12.12", "serde", "serde_json", @@ -6259,6 +6260,7 @@ dependencies = [ name = "tests_modules" version = "0.1.0" dependencies = [ + "alloy 0.7.3", "cairo-lang-starknet-classes", "cairo-vm", "dry_hint_processor", @@ -6267,6 +6269,7 @@ dependencies = [ "hints", "serde_json", "sound_hint_processor", + "starknet-crypto", "syscall_handler", "tokio", "types", diff --git a/crates/dry_hint_processor/Cargo.toml b/crates/dry_hint_processor/Cargo.toml index 1a2de84f..a787fbd5 100644 --- a/crates/dry_hint_processor/Cargo.toml +++ b/crates/dry_hint_processor/Cargo.toml @@ -9,7 +9,7 @@ cairo-lang-casm.workspace = true cairo-lang-starknet-classes.workspace = true cairo-vm.workspace = true hints.workspace = true -reqwest.workspace = true +indexer.workspace = true serde.workspace = true starknet-types-core.workspace = true starknet.workspace = true diff --git a/crates/dry_hint_processor/src/syscall_handler/starknet/header.rs b/crates/dry_hint_processor/src/syscall_handler/starknet/header.rs index b7adc042..20d0fb63 100644 --- a/crates/dry_hint_processor/src/syscall_handler/starknet/header.rs +++ b/crates/dry_hint_processor/src/syscall_handler/starknet/header.rs @@ -1,16 +1,13 @@ -use std::env; - use cairo_vm::{types::relocatable::Relocatable, vm::vm_core::VirtualMachine, Felt252}; -use reqwest::Url; +use indexer::{models::blocks, Indexer}; use syscall_handler::{traits::CallHandler, SyscallExecutionError, SyscallResult}; use types::{ cairo::{ - starknet::header::{Block, FunctionId, StarknetBlock}, + starknet::header::{FunctionId, StarknetBlock}, structs::CairoFelt, traits::CairoType, }, keys::starknet::header::{CairoKey, Key}, - RPC_URL_FEEDER_GATEWAY, STARKNET_MAINNET_CHAIN_ID, STARKNET_TESTNET_CHAIN_ID, }; #[derive(Debug, Default)] @@ -41,43 +38,19 @@ impl CallHandler for HeaderCallHandler { } async fn handle(&mut self, key: Self::Key, function_id: Self::Id, _vm: &VirtualMachine) -> SyscallResult { - let base_url = Url::parse(&env::var(RPC_URL_FEEDER_GATEWAY).unwrap()).unwrap(); - - // Feeder Gateway rejects the requests if this header is not set - let host_header = match key.chain_id { - STARKNET_MAINNET_CHAIN_ID => "alpha-mainnet.starknet.io", - STARKNET_TESTNET_CHAIN_ID => "alpha-sepolia.starknet.io", - _ => { - return Err(SyscallExecutionError::InternalError( - format!("Unknown chain id: {}", key.chain_id).into(), - )) - } - }; - - let request = reqwest::Client::new() - .get(format!("{}get_block", base_url)) - .header("Host", host_header) - .query(&[("blockNumber", key.block_number.to_string())]); - - let response = request - .send() + let provider = Indexer::default(); + + // Fetch proof response + let response = provider + .get_blocks(blocks::IndexerQuery::new( + key.chain_id, + key.block_number.into(), + key.block_number.into(), + )) .await .map_err(|e| SyscallExecutionError::InternalError(format!("Network request failed: {}", e).into()))?; - let block_data: Block = match response.status().is_success() { - true => response.json().await, - false => { - let status = response.status(); - let error_body = response.text().await.unwrap_or_default(); - return Err(SyscallExecutionError::InternalError( - format!("Request failed ({}): {}", status, error_body).into(), - )); - } - } - .map_err(|e| SyscallExecutionError::InternalError(format!("Failed to parse block data: {}", e).into()))?; - - let sn_block: StarknetBlock = block_data.into(); - - Ok(sn_block.handle(function_id)) + // Create block and handle function + Ok(StarknetBlock::from_hash_fields(response.fields).handle(function_id)) } } diff --git a/crates/fetcher/src/lib.rs b/crates/fetcher/src/lib.rs index b8a67604..1f1e993a 100644 --- a/crates/fetcher/src/lib.rs +++ b/crates/fetcher/src/lib.rs @@ -10,11 +10,7 @@ use eth_trie_proofs::{tx_receipt_trie::TxReceiptsMptHandler, tx_trie::TxsMptHand use futures::StreamExt; use indexer::models::IndexerError; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; -use proof_keys::{ - evm::{FlattenedKey, ProofKeys as EvmProofKeys}, - starknet::ProofKeys as StarknetProofKeys, - ProofKeys, -}; +use proof_keys::{evm::ProofKeys as EvmProofKeys, starknet::ProofKeys as StarknetProofKeys, FlattenedKey, ProofKeys}; use reqwest::Url; use starknet_types_core::felt::FromStrError; use syscall_handler::SyscallHandler; @@ -300,15 +296,13 @@ impl<'a> Fetcher<'a> { }) } - pub async fn collect_starknet_proofs(&self) -> Result { + async fn collect_starknet_headers_proofs( + &self, + flattened_keys: &HashSet, + ) -> Result>, FetcherError> { let mut headers_with_mmr = HashMap::default(); - let mut storages: HashSet = HashSet::default(); - - // Collect header proofs let mut header_fut = futures::stream::iter( - self.proof_keys - .starknet - .header_keys + flattened_keys .iter() .map(|key| StarknetProofKeys::fetch_header_proof(key.chain_id, key.block_number)), ) @@ -326,6 +320,16 @@ impl<'a> Fetcher<'a> { self.progress_bars.starknet_header.safe_inc(); } + Ok(headers_with_mmr) + } + + pub async fn collect_starknet_proofs(&self) -> Result { + let mut storages: HashSet = HashSet::default(); + + let flattened_keys = self.proof_keys.starknet.to_flattened_keys(); + + let headers_with_mmr = self.collect_starknet_headers_proofs(&flattened_keys).await?; + #[cfg(feature = "progress_bars")] self.progress_bars .starknet_header @@ -343,13 +347,7 @@ impl<'a> Fetcher<'a> { .boxed(); while let Some(result) = storage_fut.next().await { - let (header_with_mmr, storage) = result?; - headers_with_mmr - .entry(header_with_mmr.mmr_meta) - .and_modify(|headers: &mut Vec| headers.extend(header_with_mmr.headers.clone())) - .or_insert(header_with_mmr.headers); - storages.insert(storage); - + storages.insert(result?); #[cfg(feature = "progress_bars")] self.progress_bars.starknet_storage.safe_inc(); } diff --git a/crates/fetcher/src/proof_keys/evm.rs b/crates/fetcher/src/proof_keys/evm.rs index 170b81b5..8af54453 100644 --- a/crates/fetcher/src/proof_keys/evm.rs +++ b/crates/fetcher/src/proof_keys/evm.rs @@ -20,6 +20,7 @@ use types::{ RPC_URL_ETHEREUM, }; +use super::FlattenedKey; use crate::FetcherError; #[derive(Debug, Default)] @@ -31,12 +32,6 @@ pub struct ProofKeys { pub transaction_keys: HashSet, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct FlattenedKey { - pub chain_id: u128, - pub block_number: u64, -} - impl ProofKeys { fn normalize_hex(input: &str) -> String { let hex_str = input.trim_start_matches("0x"); diff --git a/crates/fetcher/src/proof_keys/mod.rs b/crates/fetcher/src/proof_keys/mod.rs index 192cde02..0639f273 100644 --- a/crates/fetcher/src/proof_keys/mod.rs +++ b/crates/fetcher/src/proof_keys/mod.rs @@ -1,8 +1,5 @@ use alloy::{hex::FromHexError, primitives::Bytes}; -use indexer::{ - models::{IndexerQuery, MMRProof}, - Indexer, -}; +use indexer::{models::accumulators, Indexer}; use types::proofs::mmr::MmrMeta; use crate::FetcherError; @@ -10,6 +7,12 @@ use crate::FetcherError; pub mod evm; pub mod starknet; +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct FlattenedKey { + pub chain_id: u128, + pub block_number: u64, +} + #[derive(Debug, Default)] pub struct ProofKeys { pub evm: evm::ProofKeys, @@ -22,12 +25,12 @@ impl ProofKeys { format!("{:0>width$}", hex_str, width = (hex_str.len() + 1) / 2 * 2) } - pub async fn fetch_mmr_proof(chain_id: u128, block_number: u64) -> Result<(MMRProof, MmrMeta), FetcherError> { + pub async fn fetch_mmr_proof(chain_id: u128, block_number: u64) -> Result<(accumulators::MMRProof, MmrMeta), FetcherError> { let provider = Indexer::default(); // Fetch proof response let response = provider - .get_headers_proof(IndexerQuery::new(chain_id, block_number, block_number)) + .get_headers_proof(accumulators::IndexerQuery::new(chain_id, block_number, block_number)) .await?; // Extract MMR metadata diff --git a/crates/fetcher/src/proof_keys/starknet.rs b/crates/fetcher/src/proof_keys/starknet.rs index 680a8df6..58ed2398 100644 --- a/crates/fetcher/src/proof_keys/starknet.rs +++ b/crates/fetcher/src/proof_keys/starknet.rs @@ -16,6 +16,7 @@ use types::{ RPC_URL_STARKNET, }; +use super::FlattenedKey; use crate::FetcherError; #[derive(Debug, Default)] @@ -53,7 +54,7 @@ impl ProofKeys { } } - pub async fn fetch_storage_proof(key: &keys::starknet::storage::Key) -> Result<(HeaderMmrMeta
, Storage), FetcherError> { + pub async fn fetch_storage_proof(key: &keys::starknet::storage::Key) -> Result { let response = reqwest::Client::new() .post(Url::parse(&env::var(RPC_URL_STARKNET).unwrap()).unwrap()) .json(&serde_json::json!({ @@ -81,6 +82,26 @@ impl ProofKeys { let storage = Storage::new(key.block_number, key.address, vec![key.storage_slot], proof); - Ok((ProofKeys::fetch_header_proof(key.chain_id, key.block_number).await?, storage)) + Ok(storage) + } + + pub fn to_flattened_keys(&self) -> HashSet { + let mut flattened = HashSet::new(); + + for key in &self.header_keys { + flattened.insert(FlattenedKey { + chain_id: key.chain_id, + block_number: key.block_number, + }); + } + + for key in &self.storage_keys { + flattened.insert(FlattenedKey { + chain_id: key.chain_id, + block_number: key.block_number, + }); + } + + flattened } } diff --git a/crates/indexer/Cargo.toml b/crates/indexer/Cargo.toml index 74db2f29..cb0bcb38 100644 --- a/crates/indexer/Cargo.toml +++ b/crates/indexer/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] alloy.workspace = true +cairo-vm.workspace = true reqwest.workspace = true serde_json.workspace = true serde_with.workspace = true diff --git a/crates/indexer/src/lib.rs b/crates/indexer/src/lib.rs index 566ff838..c9f7c7db 100644 --- a/crates/indexer/src/lib.rs +++ b/crates/indexer/src/lib.rs @@ -7,9 +7,10 @@ pub mod models; use std::env; -use models::{IndexerError, IndexerHeadersProofResponse, IndexerQuery, MMRResponse}; -use reqwest::Client; -use types::RPC_URL_HERODOTUS_INDEXER; +use cairo_vm::Felt252; +use models::{accumulators, blocks, IndexerError}; +use reqwest::{Client, Url}; +use types::{RPC_URL_HERODOTUS_INDEXER_GROWER, RPC_URL_HERODOTUS_INDEXER_STAGING}; #[derive(Clone)] pub struct Indexer { @@ -28,20 +29,20 @@ impl Indexer { } /// Fetch MMR and headers proof from Herodotus Indexer - pub async fn get_headers_proof(&self, query: IndexerQuery) -> Result { + pub async fn get_headers_proof(&self, query: accumulators::IndexerQuery) -> Result { + // Parse base URL from environment variable + let base_url = Url::parse(&env::var(RPC_URL_HERODOTUS_INDEXER_GROWER).unwrap()).unwrap(); + let response = self .client - .get( - env::var(RPC_URL_HERODOTUS_INDEXER) - .map_err(|e| IndexerError::GetHeadersProofError(format!("Missing RPC_URL_HERODOTUS_INDEXER env var: {}", e)))?, - ) + .get(base_url.join("/accumulators/proofs").unwrap()) .query(&query) .send() .await .map_err(IndexerError::ReqwestError)?; if response.status().is_success() { - let parsed_mmr: MMRResponse = + let parsed_mmr: accumulators::MMRResponse = serde_json::from_value(response.json().await.map_err(IndexerError::ReqwestError)?).map_err(IndexerError::SerdeJsonError)?; if parsed_mmr.data.is_empty() { @@ -55,7 +56,7 @@ impl Indexer { "Indexer didn't return the correct number of headers that were requested".to_string(), )) } else { - Ok(IndexerHeadersProofResponse::new(mmr_data)) + Ok(accumulators::IndexerProofResponse::new(mmr_data)) } } } else { @@ -64,6 +65,38 @@ impl Indexer { )) } } + + /// Fetch MMR and headers proof from Herodotus Indexer + pub async fn get_blocks(&self, query: blocks::IndexerQuery) -> Result { + // Parse base URL from environment variable + let base_url = Url::parse(&env::var(RPC_URL_HERODOTUS_INDEXER_STAGING).unwrap()).unwrap(); + + let response = self + .client + .get(base_url.join("/blocks").unwrap()) + .query(&query) + .send() + .await + .map_err(IndexerError::ReqwestError)?; + + if response.status().is_success() { + let parsed_mmr: blocks::BlocksResponse = + serde_json::from_value(response.json().await.map_err(IndexerError::ReqwestError)?).map_err(IndexerError::SerdeJsonError)?; + + let block = parsed_mmr.data.first().unwrap(); + + match &block.block_header { + models::BlockHeader::Fields(fields) => Ok(blocks::IndexerBlockResponse { + fields: fields.iter().map(|hex| Felt252::from_hex(hex).unwrap()).collect::>(), + }), + _ => Err(IndexerError::ValidationError("Invalid block header return type".to_string())), + } + } else { + Err(IndexerError::GetBlocksProofError( + response.text().await.map_err(IndexerError::ReqwestError)?, + )) + } + } } #[cfg(test)] @@ -73,7 +106,7 @@ mod tests { #[tokio::test] async fn test_get_headers_proof() { let response = Indexer::default() - .get_headers_proof(IndexerQuery::new(11155111, 5000000, 5000000)) + .get_headers_proof(accumulators::IndexerQuery::new(11155111, 5000000, 5000000)) .await .unwrap(); assert_eq!(response.headers.len(), 1); @@ -82,7 +115,7 @@ mod tests { #[tokio::test] async fn test_get_headers_proof_multiple_blocks() { let response = Indexer::default() - .get_headers_proof(IndexerQuery::new(11155111, 5800000, 5800010)) + .get_headers_proof(accumulators::IndexerQuery::new(11155111, 5800000, 5800010)) .await .unwrap(); assert_eq!(response.headers.len(), 11); diff --git a/crates/indexer/src/models.rs b/crates/indexer/src/models/accumulators.rs similarity index 68% rename from crates/indexer/src/models.rs rename to crates/indexer/src/models/accumulators.rs index ddc4b90c..a2e698fe 100644 --- a/crates/indexer/src/models.rs +++ b/crates/indexer/src/models/accumulators.rs @@ -3,16 +3,8 @@ use std::collections::HashMap; use alloy::primitives::BlockNumber; use serde::{Deserialize, Serialize}; use serde_with::serde_as; -use thiserror::Error; -/// Enum for available hashing functions -#[derive(Debug, Serialize)] -#[serde(rename_all = "lowercase")] -pub enum HashingFunction { - Keccak, - Poseidon, - Pedersen, -} +use super::{BlockHeader, HashingFunction}; /// Enum for available contract types #[derive(Debug, Serialize)] @@ -23,30 +15,6 @@ pub enum ContractType { Remapper, } -/// Error from [`Indexer`] -#[derive(Error, Debug)] -pub enum IndexerError { - /// The block range provided is invalid. - #[error("Invalid block range")] - InvalidBlockRange, - - /// Failed to send a request using [`reqwest`]. - #[error("Failed to send request")] - ReqwestError(#[from] reqwest::Error), - - /// Failed to parse the response using [`serde_json`]. - #[error("Failed to parse response")] - SerdeJsonError(#[from] serde_json::Error), - - /// Validation error with a detailed message. - #[error("Validation error: {0}")] - ValidationError(String), - - /// Failed to get headers proof with a detailed message. - #[error("Failed to get headers proof: {0}")] - GetHeadersProofError(String), -} - /// Query parameters for the indexer #[derive(Debug, Serialize)] pub struct IndexerQuery { @@ -112,20 +80,13 @@ pub struct MMRProof { pub siblings_hashes: Vec, } -#[derive(Serialize, Deserialize, Debug, Clone)] -pub enum BlockHeader { - RlpString(String), - RlpLittleEndian8ByteChunks(Vec), - Fields(Vec), -} - #[derive(Debug)] -pub struct IndexerHeadersProofResponse { +pub struct IndexerProofResponse { pub mmr_meta: MMRMetadata, pub headers: HashMap, } -impl IndexerHeadersProofResponse { +impl IndexerProofResponse { pub fn new(mmr_data: MMRData) -> Self { let mmr_meta = mmr_data.meta; let headers = mmr_data.proofs.into_iter().map(|block| (block.block_number, block)).collect(); diff --git a/crates/indexer/src/models/blocks.rs b/crates/indexer/src/models/blocks.rs new file mode 100644 index 00000000..fb688318 --- /dev/null +++ b/crates/indexer/src/models/blocks.rs @@ -0,0 +1,55 @@ +use cairo_vm::Felt252; +use serde::{Deserialize, Serialize}; + +use super::{BlockHeader, HashingFunction}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub enum Sorting { + Ascending, + Descending, +} + +/// Query parameters for the indexer +#[derive(Debug, Serialize)] +pub struct IndexerQuery { + pub chain_id: u128, + pub hashing_function: HashingFunction, + pub from_block_number_inclusive: u128, + pub to_block_number_inclusive: u128, + pub sort: Sorting, +} + +impl IndexerQuery { + pub fn new(chain_id: u128, from_block: u128, to_block: u128) -> Self { + Self { + chain_id, + hashing_function: HashingFunction::Poseidon, + from_block_number_inclusive: from_block, + to_block_number_inclusive: to_block, + sort: Sorting::Ascending, + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Block { + pub block_number: u64, + pub block_header: BlockHeader, + pub block_hash: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct BlocksResponse { + pub data: Vec, +} + +#[derive(Debug)] +pub struct IndexerBlockResponse { + pub fields: Vec, +} + +impl IndexerBlockResponse { + pub fn new(fields: Vec) -> Self { + Self { fields } + } +} diff --git a/crates/indexer/src/models/mod.rs b/crates/indexer/src/models/mod.rs new file mode 100644 index 00000000..8ff0dedf --- /dev/null +++ b/crates/indexer/src/models/mod.rs @@ -0,0 +1,49 @@ +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +pub mod accumulators; +pub mod blocks; + +/// Enum for available hashing functions +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum HashingFunction { + Keccak, + Poseidon, + Pedersen, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum BlockHeader { + RlpString(String), + RlpLittleEndian8ByteChunks(Vec), + Fields(Vec), +} + +/// Error from [`Indexer`] +#[derive(Error, Debug)] +pub enum IndexerError { + /// The block range provided is invalid. + #[error("Invalid block range")] + InvalidBlockRange, + + /// Failed to send a request using [`reqwest`]. + #[error("Failed to send request")] + ReqwestError(#[from] reqwest::Error), + + /// Failed to parse the response using [`serde_json`]. + #[error("Failed to parse response")] + SerdeJsonError(#[from] serde_json::Error), + + /// Validation error with a detailed message. + #[error("Validation error: {0}")] + ValidationError(String), + + /// Failed to get headers proof with a detailed message. + #[error("Failed to get headers proof: {0}")] + GetHeadersProofError(String), + + /// Failed to get blocks with a detailed message. + #[error("Failed to get blocks: {0}")] + GetBlocksProofError(String), +} diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 8a21486d..29244eb6 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -15,8 +15,8 @@ use proofs::{evm, starknet}; use serde::{Deserialize, Serialize}; pub const RPC_URL_ETHEREUM: &str = "RPC_URL_ETHEREUM"; -pub const RPC_URL_FEEDER_GATEWAY: &str = "RPC_URL_FEEDER_GATEWAY"; -pub const RPC_URL_HERODOTUS_INDEXER: &str = "RPC_URL_HERODOTUS_INDEXER"; +pub const RPC_URL_HERODOTUS_INDEXER_GROWER: &str = "RPC_URL_HERODOTUS_INDEXER_GROWER"; +pub const RPC_URL_HERODOTUS_INDEXER_STAGING: &str = "RPC_URL_HERODOTUS_INDEXER_STAGING"; pub const RPC_URL_STARKNET: &str = "RPC_URL_STARKNET"; pub const ETHEREUM_MAINNET_CHAIN_ID: u128 = 0x1; diff --git a/tests/Cargo.toml b/tests/Cargo.toml index e806c9c4..119fdf44 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] [dev-dependencies] +alloy.workspace = true cairo-lang-starknet-classes.workspace = true cairo-vm.workspace = true dry_hint_processor.workspace = true @@ -14,6 +15,7 @@ futures.workspace = true hints.workspace = true serde_json.workspace = true sound_hint_processor.workspace = true +starknet-crypto.workspace = true tokio.workspace = true types.workspace = true syscall_handler.workspace = true \ No newline at end of file diff --git a/tests/src/hashers.cairo b/tests/src/hashers.cairo new file mode 100644 index 00000000..4a42ef03 --- /dev/null +++ b/tests/src/hashers.cairo @@ -0,0 +1,3 @@ +pub mod poseidon_modules; +pub mod keccak_modules; +pub mod pedersen_modules; diff --git a/tests/src/hashers/keccak_modules.cairo b/tests/src/hashers/keccak_modules.cairo new file mode 100644 index 00000000..fd33e62f --- /dev/null +++ b/tests/src/hashers/keccak_modules.cairo @@ -0,0 +1,17 @@ +#[starknet::contract] +mod hashers_keccak { + use core::keccak::{keccak_u256s_be_inputs}; + use hdp_cairo::{HDP}; + + #[storage] + struct Storage {} + + #[external(v0)] + pub fn main(ref self: ContractState, hdp: HDP) { + let hash1 = keccak_u256s_be_inputs(array![1, 2, 3, 4].span()); + let hash2 = keccak_u256s_be_inputs(array![1, 2, 3, 5].span()); + + assert!(hash1 == 0x2d9982dfaf468a9ddf7101b6323aa9d56510e6fd534f267a01086462df912739); + assert!(hash2 == 0x67cebf8d7d4a744b86437de146253d74fd06da9cd1a25494a707bd32c2d98bbd); + } +} diff --git a/tests/src/hashers/mod.rs b/tests/src/hashers/mod.rs new file mode 100644 index 00000000..a5bb0469 --- /dev/null +++ b/tests/src/hashers/mod.rs @@ -0,0 +1,43 @@ +use alloy::{ + hex::FromHex, + primitives::{keccak256, FixedBytes, U256}, +}; +use cairo_vm::Felt252; +use starknet_crypto::{pedersen_hash, poseidon_hash_many}; + +pub mod tests_runner; + +#[tokio::test] +async fn test_poseidon_hash_rust() { + let hash1 = poseidon_hash_many(&[Felt252::from(1), Felt252::from(2), Felt252::from(3), Felt252::from(4)]); + let hash2 = poseidon_hash_many(&[Felt252::from(1), Felt252::from(2), Felt252::from(3), Felt252::from(5)]); + + assert!(hash1 == Felt252::from_hex("0x26e3ad8b876e02bc8a4fc43dad40a8f81a6384083cabffa190bcf40d512ae1d").unwrap()); + assert!(hash2 == Felt252::from_hex("0x57b091966b9a59d46d961b416376fadeb9b0755fabe4d3b63bed65a613c9f3f").unwrap()); +} + +#[tokio::test] +async fn test_keccak_hash_rust() { + let values = vec![U256::from(1), U256::from(2), U256::from(3), U256::from(4)]; + let bytes: Vec = values.iter().flat_map(|v| v.to_be_bytes::<32>()).collect(); + let mut hash1 = keccak256(bytes.as_slice()); + hash1.reverse(); + + assert!(hash1 == FixedBytes::from_hex("0x2d9982dfaf468a9ddf7101b6323aa9d56510e6fd534f267a01086462df912739").unwrap()); + + let values2 = vec![U256::from(1), U256::from(2), U256::from(3), U256::from(5)]; + let bytes2: Vec = values2.iter().flat_map(|v| v.to_be_bytes::<32>()).collect(); + let mut hash2 = keccak256(bytes2.as_slice()); + hash2.reverse(); + + assert!(hash2 == FixedBytes::from_hex("0x67cebf8d7d4a744b86437de146253d74fd06da9cd1a25494a707bd32c2d98bbd").unwrap()); +} + +#[tokio::test] +async fn test_perdersen_hash_rust() { + let hash1 = pedersen_hash(&Felt252::from_bytes_le_slice(&[1]), &Felt252::from_bytes_le_slice(&[2])); + let hash2 = pedersen_hash(&Felt252::from_bytes_le_slice(&[3]), &Felt252::from_bytes_le_slice(&[4])); + + assert!(hash1 == Felt252::from_hex("0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026").unwrap()); + assert!(hash2 == Felt252::from_hex("0x262697b88544f733e5c6907c3e1763131e9f14c51ee7951258abbfb29415fbf").unwrap()); +} diff --git a/tests/src/hashers/pedersen_modules.cairo b/tests/src/hashers/pedersen_modules.cairo new file mode 100644 index 00000000..b55b3ebe --- /dev/null +++ b/tests/src/hashers/pedersen_modules.cairo @@ -0,0 +1,17 @@ +#[starknet::contract] +mod hashers_pedersen { + use core::pedersen::{pedersen}; + use hdp_cairo::{HDP}; + + #[storage] + struct Storage {} + + #[external(v0)] + pub fn main(ref self: ContractState, hdp: HDP) { + let hash1 = pedersen(1, 2); + let hash2 = pedersen(3, 4); + + assert!(hash1 == 0x5bb9440e27889a364bcb678b1f679ecd1347acdedcbf36e83494f857cc58026); + assert!(hash2 == 0x262697b88544f733e5c6907c3e1763131e9f14c51ee7951258abbfb29415fbf); + } +} diff --git a/tests/src/hashers/poseidon_modules.cairo b/tests/src/hashers/poseidon_modules.cairo new file mode 100644 index 00000000..d2511e72 --- /dev/null +++ b/tests/src/hashers/poseidon_modules.cairo @@ -0,0 +1,36 @@ +#[starknet::contract] +mod hashers_poseidon { + use core::poseidon::{poseidon_hash_span, PoseidonImpl}; + use core::hash::{HashStateTrait}; + use hdp_cairo::{HDP}; + + #[storage] + struct Storage {} + + #[external(v0)] + pub fn main(ref self: ContractState, hdp: HDP) { + let hash1 = poseidon_hash_span(array![1, 2, 3, 4].span()); + let mut hash1_alternative = PoseidonImpl::new() + .update(1) + .update(2) + .update(3) + .update(4) + .finalize(); + let hash2 = poseidon_hash_span(array![1, 2, 3, 5].span()); + let mut hash2_alternative = PoseidonImpl::new() + .update(1) + .update(2) + .update(3) + .update(5) + .finalize(); + + assert!(hash1 == 0x26e3ad8b876e02bc8a4fc43dad40a8f81a6384083cabffa190bcf40d512ae1d); + assert!(hash2 == 0x57b091966b9a59d46d961b416376fadeb9b0755fabe4d3b63bed65a613c9f3f); + assert!( + hash1_alternative == 0x26e3ad8b876e02bc8a4fc43dad40a8f81a6384083cabffa190bcf40d512ae1d, + ); + assert!( + hash2_alternative == 0x57b091966b9a59d46d961b416376fadeb9b0755fabe4d3b63bed65a613c9f3f, + ); + } +} diff --git a/tests/src/hashers/tests_runner.rs b/tests/src/hashers/tests_runner.rs new file mode 100644 index 00000000..eab56801 --- /dev/null +++ b/tests/src/hashers/tests_runner.rs @@ -0,0 +1,28 @@ +use crate::test_utils::run; + +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn test_poseidon_hash() { + run(serde_json::from_slice(include_bytes!( + "../../../target/dev/modules_hashers_poseidon.compiled_contract_class.json" + )) + .unwrap()) + .await +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn test_keccak_hash() { + run(serde_json::from_slice(include_bytes!( + "../../../target/dev/modules_hashers_keccak.compiled_contract_class.json" + )) + .unwrap()) + .await +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn test_pedersen_hash() { + run(serde_json::from_slice(include_bytes!( + "../../../target/dev/modules_hashers_pedersen.compiled_contract_class.json" + )) + .unwrap()) + .await +} diff --git a/tests/src/lib.cairo b/tests/src/lib.cairo index 99fa67e9..5cd650b3 100644 --- a/tests/src/lib.cairo +++ b/tests/src/lib.cairo @@ -1,3 +1,4 @@ pub mod evm; pub mod starknet; pub mod utils; +pub mod hashers; diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 8b6c7548..8a2ed32e 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -10,6 +10,9 @@ pub mod starknet; #[cfg(test)] pub mod utils; +#[cfg(test)] +pub mod hashers; + #[cfg(test)] mod test_utils { use std::{env, path::PathBuf};