Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
986 changes: 729 additions & 257 deletions Cargo.lock

Large diffs are not rendered by default.

24 changes: 12 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,33 @@ members = [
]

[workspace.package]
version = "0.2.0"
version = "0.3.0"
edition = "2021"
rust-version = "1.89.0"
rust-version = "1.95.0"
repository = "https://github.com/keep-starknet-strange/snos/"
license-file = "LICENSE"

[workspace.dependencies]
# 0.14.2 baseline: use the published Apollo release tag for the 0.14.2 line.
starknet_api = { git = "https://github.com/starkware-libs/sequencer", tag = "APOLLO-0.14.2-RC.7-fix", features = [
# 0.14.3 baseline: use the published Apollo release tag for the 0.14.3 line.
starknet_api = { git = "https://github.com/starkware-libs/sequencer", tag = "APOLLO-0.14.3-RC.15", features = [
"testing",
] }
starknet_os = { git = "https://github.com/starkware-libs/sequencer", tag = "APOLLO-0.14.2-RC.7-fix", features = [
starknet_os = { git = "https://github.com/starkware-libs/sequencer", tag = "APOLLO-0.14.3-RC.15", features = [
"testing",
] }
blockifier = { git = "https://github.com/starkware-libs/sequencer", tag = "APOLLO-0.14.2-RC.7-fix", features = [
blockifier = { git = "https://github.com/starkware-libs/sequencer", tag = "APOLLO-0.14.3-RC.15", features = [
"testing",
"reexecution",
] }
starknet_patricia = { git = "https://github.com/starkware-libs/sequencer", tag = "APOLLO-0.14.2-RC.7-fix", features = [
starknet_patricia = { git = "https://github.com/starkware-libs/sequencer", tag = "APOLLO-0.14.3-RC.15", features = [
"testing",
] }
shared_execution_objects = { git = "https://github.com/starkware-libs/sequencer", tag = "APOLLO-0.14.2-RC.7-fix" }
shared_execution_objects = { git = "https://github.com/starkware-libs/sequencer", tag = "APOLLO-0.14.3-RC.15" }

cairo-lang-starknet = "=2.17.0"
cairo-lang-starknet-classes = "=2.17.0"
cairo-lang-utils = "=2.17.0"
cairo-lang-casm = "=2.17.0"
cairo-lang-starknet = "=2.19.4"
cairo-lang-starknet-classes = "=2.19.4"
cairo-lang-utils = "=2.19.4"
cairo-lang-casm = "=2.19.4"
cairo-vm = "=3.2.0"

starknet = {package = "starknet-rust", version = "0.19.0-rc.2"}
Expand Down
7 changes: 5 additions & 2 deletions crates/core/generate-pie/src/block_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use log::info;
use rpc_client::RpcClient;
use starknet::core::types::BlockId;
use starknet_api::block::BlockHash;
use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress};
use starknet_api::core::{ChainId, ClassHash, CompiledClassHash, ContractAddress};
use starknet_api::deprecated_contract_class::ContractClass;
use starknet_os::io::os_input::OsBlockInput;
use starknet_types_core::felt::Felt;
Expand All @@ -29,6 +29,8 @@ use std::collections::BTreeMap;
pub struct BlockInfoResult {
/// The OS block input for the block.
pub os_block_input: OsBlockInput,
/// The chain ID reported by the RPC provider.
pub chain_id: ChainId,
/// Compiled classes used in the block.
pub compiled_classes: BTreeMap<CompiledClassHash, CasmContractClass>,
/// Deprecated compiled classes used in the block.
Expand Down Expand Up @@ -134,7 +136,7 @@ pub async fn collect_single_block_info(

info!("Successfully completed construction of OsBlockInput for block {}", block_number);

Ok(BlockInfoResult { os_block_input, compiled_classes, deprecated_compiled_classes })
Ok(BlockInfoResult { os_block_input, chain_id: block_data.chain_id, compiled_classes, deprecated_compiled_classes })
}

// ================================================================================================
Expand All @@ -160,6 +162,7 @@ fn build_os_block_input(
tx_execution_infos: tx_result.central_txn_execution_infos,
declared_class_hash_to_component_hashes: class_result.declared_class_hash_component_hashes,
block_info: block_context.block_info().clone(),
starknet_version_override: None,
block_hash_commitments: tx_result.block_hash_commitments,
prev_block_hash: block_data
.previous_block
Expand Down
27 changes: 24 additions & 3 deletions crates/core/generate-pie/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ use cairo_vm::types::layout_name::LayoutName;
use futures::future::join_all;
use log::{info, warn};
use rpc_client::RpcClient;
use starknet_api::core::OsChainInfo;
use starknet_api::core::{ChainId, OsChainInfo};
use starknet_os::{
io::os_input::{OsHints, OsHintsConfig, StarknetOsInput},
runner::run_os_stateless,
Expand Down Expand Up @@ -206,6 +206,7 @@ pub async fn generate_pie(input: PieGenerationInput) -> Result<PieGenerationResu

Ok::<_, PieGenerationError>((
block_info.os_block_input,
block_info.chain_id,
block_info.compiled_classes,
block_info.deprecated_compiled_classes,
))
Expand All @@ -220,10 +221,23 @@ pub async fn generate_pie(input: PieGenerationInput) -> Result<PieGenerationResu
let mut os_block_inputs = Vec::new();
let mut compiled_classes = std::collections::BTreeMap::new();
let mut deprecated_compiled_classes = std::collections::BTreeMap::new();
let mut provider_chain_id: Option<ChainId> = None;

for result in results {
// First unwrap the JoinHandle, then unwrap the inner Result
let (block_input, block_compiled_classes, block_deprecated_compiled_classes) = result??;
let (block_input, block_chain_id, block_compiled_classes, block_deprecated_compiled_classes) =
result.map_err(|e| PieGenerationError::RpcClient(format!("Task join error: {:?}", e)))??;

if let Some(provider_chain_id) = &provider_chain_id {
if provider_chain_id != &block_chain_id {
return Err(PieGenerationError::InvalidConfig(format!(
"Blocks belong to different chains: {:?} and {:?}",
provider_chain_id, block_chain_id
)));
}
} else {
provider_chain_id = Some(block_chain_id);
}

// Add block input to our collection
os_block_inputs.push(block_input);
Expand All @@ -245,6 +259,13 @@ pub async fn generate_pie(input: PieGenerationInput) -> Result<PieGenerationResu

info!("=== Finalizing multi-block processing ===");
info!("OS inputs prepared with {} block inputs", os_block_inputs.len());
let os_chain_id = provider_chain_id.unwrap_or_else(|| input.chain_config.chain_id.clone());
if os_chain_id != input.chain_config.chain_id {
warn!(
"Using provider chain_id {:?} for OS hints instead of configured chain_id {:?}",
os_chain_id, input.chain_config.chain_id
);
}

// Build OS hints configuration
info!("Building OS hints configuration for multi-block processing");
Expand All @@ -254,7 +275,7 @@ pub async fn generate_pie(input: PieGenerationInput) -> Result<PieGenerationResu
full_output: input.os_hints_config.full_output,
use_kzg_da: input.os_hints_config.use_kzg_da,
chain_info: OsChainInfo {
chain_id: input.chain_config.chain_id,
chain_id: os_chain_id,
strk_fee_token_address: input.chain_config.strk_fee_token_address,
},
public_keys: input.public_keys.clone(),
Expand Down
7 changes: 3 additions & 4 deletions crates/core/generate-pie/src/utils/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,10 @@ pub fn sort_abi_entries_for_deprecated_class(
if let Some(attr_obj) = attr.as_object_mut() {
// Remove empty accessible_scopes arrays
match attr_obj.get("accessible_scopes") {
Some(serde_json::Value::Array(array)) => {
if array.is_empty() {
attr_obj.remove("accessible_scopes");
}
Some(serde_json::Value::Array(array)) if array.is_empty() => {
attr_obj.remove("accessible_scopes");
}
Some(serde_json::Value::Array(_)) => {}
Some(_) => {
return Err("Program attribute 'accessible_scopes' was not an array type".into());
}
Expand Down
34 changes: 33 additions & 1 deletion crates/rpc-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ const RPC_REQUEST_TIMEOUT_ENV: &str = "SNOS_RPC_REQUEST_TIMEOUT_SECS";
const RPC_CONNECT_TIMEOUT_ENV: &str = "SNOS_RPC_CONNECT_TIMEOUT_SECS";
const RPC_POOL_MAX_IDLE_PER_HOST_ENV: &str = "SNOS_RPC_POOL_MAX_IDLE_PER_HOST";

fn starknet_rpc_url(base_url: &str) -> String {
let base_url = base_url.trim_end_matches('/');
let rpc_suffix = format!("/rpc/{}", STARKNET_RPC_VERSION);

if base_url.ends_with(&rpc_suffix) {
base_url.to_owned()
} else {
format!("{base_url}{rpc_suffix}")
}
}

pub trait ProofClient {
fn get_proof(
&self,
Expand Down Expand Up @@ -86,7 +97,7 @@ impl RpcClientInner {
/// let inner = RpcClientInner::new("https://your-starknet-node.com");
/// ```
fn try_new(base_url: &str) -> anyhow::Result<Self> {
let starknet_rpc_url = format!("{}/rpc/{}", base_url, STARKNET_RPC_VERSION);
let starknet_rpc_url = starknet_rpc_url(base_url);
info!("Initializing Starknet RPC client with URL: {}", starknet_rpc_url);

let rpc_request_timeout_secs = Self::read_env_u64(RPC_REQUEST_TIMEOUT_ENV, DEFAULT_RPC_REQUEST_TIMEOUT_SECS);
Expand All @@ -113,6 +124,27 @@ impl RpcClientInner {
}
}

#[cfg(test)]
mod url_tests {
use super::starknet_rpc_url;

#[test]
fn appends_rpc_version_to_base_url() {
assert_eq!(starknet_rpc_url("https://example.com/path"), "https://example.com/path/rpc/v0_10");
}

#[test]
fn keeps_already_versioned_rpc_url() {
assert_eq!(starknet_rpc_url("https://example.com/path/rpc/v0_10"), "https://example.com/path/rpc/v0_10");
}

#[test]
fn trims_trailing_slashes_before_normalizing() {
assert_eq!(starknet_rpc_url("https://example.com/path/"), "https://example.com/path/rpc/v0_10");
assert_eq!(starknet_rpc_url("https://example.com/path/rpc/v0_10/"), "https://example.com/path/rpc/v0_10");
}
}

/// A unified RPC client for interacting with Starknet nodes.
///
/// This client provides access to both standard Starknet RPC endpoints and Pathfinder-specific
Expand Down
2 changes: 1 addition & 1 deletion crates/rpc-replay/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rpc-replay"
version = "0.1.0"
version.workspace = true
edition = "2021"

[[bin]]
Expand Down
43 changes: 40 additions & 3 deletions crates/rpc-replay/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ RUN apt-get update && apt-get install -y \
python3-dev \
git \
ca-certificates \
wget \
gnupg \
&& rm -rf /var/lib/apt/lists/*

# Install Rust for linux/amd64.
# Keep the image builder aligned with the workspace toolchain used for local validation.
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.89 \
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.95 \
&& . /usr/local/cargo/env \
&& rustup target add x86_64-unknown-linux-gnu

Expand Down Expand Up @@ -56,10 +58,34 @@ RUN if find /usr/local/cargo/git/checkouts -name "requirements.txt" -path "*/scr
echo "No requirements.txt found, skipping pip install"; \
fi

# Install the LLVM/MLIR toolchain required by cairo-native.
RUN wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key \
| gpg --dearmor -o /usr/share/keyrings/llvm-archive-keyring.gpg \
&& echo "deb [signed-by=/usr/share/keyrings/llvm-archive-keyring.gpg] https://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main" \
> /etc/apt/sources.list.d/llvm.list \
&& apt-get update \
&& apt-get install -y \
llvm-19 \
llvm-19-dev \
llvm-19-runtime \
clang-19 \
clang-tools-19 \
libclang-19-dev \
lld-19 \
libpolly-19-dev \
libmlir-19-dev \
mlir-19-tools \
libzstd-dev \
&& rm -rf /var/lib/apt/lists/*

ENV MLIR_SYS_190_PREFIX=/usr/lib/llvm-19
ENV LLVM_SYS_191_PREFIX=/usr/lib/llvm-19
ENV TABLEGEN_190_PREFIX=/usr/lib/llvm-19

# Build the rpc-replay binary
RUN . sequencer_venv/bin/activate \
&& . /usr/local/cargo/env \
&& cargo build --release --bin rpc-replay
&& RUSTFLAGS="-C link-arg=-fuse-ld=lld" cargo build --bin rpc-replay

# Runtime stage
FROM --platform=linux/amd64 ubuntu:22.04
Expand All @@ -71,17 +97,27 @@ RUN apt-get update && apt-get install -y \
libgmp10 \
python3 \
python3-venv \
wget \
gnupg \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean

RUN wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key \
| gpg --dearmor -o /usr/share/keyrings/llvm-archive-keyring.gpg \
&& echo "deb [signed-by=/usr/share/keyrings/llvm-archive-keyring.gpg] https://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main" \
> /etc/apt/sources.list.d/llvm.list \
&& apt-get update \
&& apt-get install -y llvm-19-runtime libmlir-19 \
&& rm -rf /var/lib/apt/lists/*

# Create a non-root user
RUN useradd -m -u 1000 appuser

# Create directories for logs and output
RUN mkdir -p /app/output /app/logs && chown -R appuser:appuser /app

# Copy the binary from builder stage
COPY --from=builder /app/target/release/rpc-replay /usr/local/bin/rpc-replay
COPY --from=builder /app/target/debug/rpc-replay /usr/local/bin/rpc-replay

# Copy Python virtual environment
COPY --from=builder /app/sequencer_venv /app/sequencer_venv
Expand All @@ -107,6 +143,7 @@ ENV AWS_S3_BUCKET=""
ENV AWS_ACCESS_KEY_ID=""
ENV AWS_SECRET_ACCESS_KEY=""
ENV PATH=/app/sequencer_venv/bin:$PATH
ENV LD_LIBRARY_PATH=/usr/lib/llvm-19/lib

# Expose volumes for output files and logs
VOLUME ["/app/output", "/app/logs"]
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ autoflake==2.3.1
bitarray==3.5.2
black==23.1a1
cachetools==6.1.0
cairo-lang==0.14.1a0
cairo-lang==0.14.3a3
certifi==2025.7.14
charset-normalizer==3.4.2
ckzg==2.1.1
Expand Down Expand Up @@ -79,4 +79,4 @@ typing_extensions==4.14.1
urllib3==2.5.0
web3==7.12.1
websockets==15.0.1
yarl==1.20.1
yarl==1.20.1
2 changes: 1 addition & 1 deletion rust-toolchain
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[toolchain]
channel = "1.89"
channel = "1.95"
components = ["rustfmt", "clippy"]
profile = "minimal"
Loading