Skip to content
Open
Changes from 1 commit
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
62 changes: 57 additions & 5 deletions crates/core/generate-pie/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
//! This binary demonstrates how to use the generate-pie library to generate
//! Cairo PIE files from Starknet blocks.

use std::collections::BTreeSet;

use cairo_vm::types::layout_name::LayoutName;
use clap::Parser;
use generate_pie::constants::{DEFAULT_SEPOLIA_ETH_FEE_TOKEN, DEFAULT_SEPOLIA_STRK_FEE_TOKEN};
Expand All @@ -11,15 +13,43 @@ use generate_pie::utils::load_versioned_constants;
use generate_pie::{generate_pie, parse_layout};
use log::{error, info};

/// Parses a range string in format "start,end" and returns (start, end).
/// Both start and end are inclusive.
fn parse_range(range_str: &str) -> Result<(u64, u64), String> {
let parts: Vec<&str> = range_str.split(',').collect();
if parts.len() != 2 {
return Err(format!("Invalid range format '{}'. Expected format: start,end (e.g., 1,999)", range_str));
}

let start: u64 = parts[0]
.trim()
.parse()
.map_err(|_| format!("Invalid start value '{}'. Must be a positive integer.", parts[0]))?;
let end: u64 = parts[1]
.trim()
.parse()
.map_err(|_| format!("Invalid end value '{}'. Must be a positive integer.", parts[1]))?;

if start > end {
return Err(format!("Invalid range: start ({}) must be less than or equal to end ({})", start, end));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return Err(format!("Invalid range: start ({}) must be less than or equal to end ({})", start, end));
return Err(format!("Invalid range: start ({}) must be less than or equal to end ({}).", start, end));

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented on current head: BlockRange::from_str rejects start > end and is covered by test block_range_rejects_start_greater_than_end.

}

Ok((start, end))
}

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
#[command(name = "snos")]
#[command(about = "SNOS - Starknet OS for block processing")]
struct Cli {
/// Block number(s) to process
#[arg(short, long, value_delimiter = ',', required = true, env = "SNOS_BLOCKS")]
/// Block number(s) to process (comma-separated)
#[arg(short, long, value_delimiter = ',', env = "SNOS_BLOCKS")]
blocks: Vec<u64>,

/// Block range to process (format: start,end - inclusive)
#[arg(short = 'R', long, env = "SNOS_RANGE")]
range: Option<String>,

/// RPC URL to connect to
#[arg(short, long, required = true, env = "SNOS_RPC_URL")]
rpc_url: String,
Expand Down Expand Up @@ -82,9 +112,31 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {

info!("Starting SNOS PIE generation application");

// Collect blocks from --blocks and --range arguments
let mut blocks: BTreeSet<u64> = cli.blocks.into_iter().collect();

// Parse and add blocks from range if provided
if let Some(range_str) = &cli.range {
match parse_range(range_str) {
Ok((start, end)) => {
info!("Adding blocks from range {} to {} (inclusive)", start, end);
for block in start..=end {
blocks.insert(block);
}
}
Err(e) => {
error!("Range parsing error: {}", e);
std::process::exit(1);
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic could be simplified letting CLAP managing the deserialization of the arguments by having a BlockRange type + accepting an Option<BlockRange>.

But the current logic works perfectly fine. 👍

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented: CLI now uses Option with Clap deserialization. Added test cli_parses_range_argument_into_block_range.


// Convert to sorted Vec
let blocks: Vec<u64> = blocks.into_iter().collect();

// Validate that at least one block is provided
if cli.blocks.is_empty() {
error!("At least one block number must be provided");
if blocks.is_empty() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be done on the BTreeSet before pending time on converting to vec.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented via collect_blocks: emptiness is validated on BTreeSet before conversion to Vec, with tests for merge/dedupe and empty input.

error!("At least one block number must be provided. Use --blocks and/or --range.");
std::process::exit(1);
}

Expand All @@ -97,7 +149,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// Build the input configuration
let input = PieGenerationInput {
rpc_url: cli.rpc_url.clone(),
blocks: cli.blocks.clone(),
blocks: blocks.clone(),
chain_config: ChainConfig::new(&cli.chain, &cli.strk_fee_token_address, &cli.eth_fee_token_address, cli.is_l3),
os_hints_config: OsHintsConfiguration::default_with_is_l3(cli.is_l3),
output_path: cli.output.clone(),
Expand Down