Skip to content

Commit ab24518

Browse files
committed
feat(solis): update katana for solis
1 parent 5a7ecd8 commit ab24518

File tree

17 files changed

+1015
-35
lines changed

17 files changed

+1015
-35
lines changed

bin/solis/Cargo.toml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
[package]
2+
description = "A fast and lightweight local Starknet development sequencer."
3+
edition.workspace = true
4+
license-file.workspace = true
5+
name = "solis"
6+
repository.workspace = true
7+
version.workspace = true
8+
9+
[dependencies]
10+
alloy-primitives.workspace = true
11+
anyhow.workspace = true
12+
cfg-if = "1.0.0"
13+
clap.workspace = true
14+
clap_complete.workspace = true
15+
common.workspace = true
16+
console.workspace = true
17+
dojo-metrics.workspace = true
18+
katana-core.workspace = true
19+
katana-executor.workspace = true
20+
katana-primitives.workspace = true
21+
katana-rpc-api.workspace = true
22+
katana-rpc.workspace = true
23+
serde_json.workspace = true
24+
shellexpand = "3.1.0"
25+
starknet_api.workspace = true
26+
tokio.workspace = true
27+
tracing-subscriber.workspace = true
28+
tracing.workspace = true
29+
url.workspace = true
30+
31+
[dev-dependencies]
32+
assert_matches = "1.5.0"
33+
34+
[features]
35+
default = [ "blockifier", "jemalloc", "messaging" ]
36+
37+
blockifier = [ "katana-executor/blockifier" ]
38+
# Disable until SIR support Cairo 2.6.3
39+
#sir = [ "katana-executor/sir" ]
40+
41+
jemalloc = [ "dojo-metrics/jemalloc" ]
42+
messaging = [ "katana-core/messaging" ]
43+
starknet-messaging = [ "katana-core/starknet-messaging", "messaging" ]

bin/solis/src/args.rs

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
//! Katana binary executable.
2+
//!
3+
//! ## Feature Flags
4+
//!
5+
//! - `jemalloc`: Uses [jemallocator](https://github.com/tikv/jemallocator) as the global allocator.
6+
//! This is **not recommended on Windows**. See [here](https://rust-lang.github.io/rfcs/1974-global-allocators.html#jemalloc)
7+
//! for more info.
8+
//! - `jemalloc-prof`: Enables [jemallocator's](https://github.com/tikv/jemallocator) heap profiling
9+
//! and leak detection functionality. See [jemalloc's opt.prof](https://jemalloc.net/jemalloc.3.html#opt.prof)
10+
//! documentation for usage details. This is **not recommended on Windows**. See [here](https://rust-lang.github.io/rfcs/1974-global-allocators.html#jemalloc)
11+
//! for more info.
12+
13+
use std::net::SocketAddr;
14+
use std::path::PathBuf;
15+
16+
use alloy_primitives::U256;
17+
use clap::{Args, Parser, Subcommand};
18+
use clap_complete::Shell;
19+
use common::parse::parse_socket_address;
20+
use katana_core::backend::config::{Environment, StarknetConfig};
21+
use katana_core::constants::{
22+
DEFAULT_ETH_L1_GAS_PRICE, DEFAULT_INVOKE_MAX_STEPS, DEFAULT_SEQUENCER_ADDRESS,
23+
DEFAULT_STRK_L1_GAS_PRICE, DEFAULT_VALIDATE_MAX_STEPS,
24+
};
25+
use katana_core::sequencer::SequencerConfig;
26+
use katana_primitives::block::GasPrices;
27+
use katana_primitives::chain::ChainId;
28+
use katana_primitives::genesis::allocation::DevAllocationsGenerator;
29+
use katana_primitives::genesis::constant::DEFAULT_PREFUNDED_ACCOUNT_BALANCE;
30+
use katana_primitives::genesis::Genesis;
31+
use katana_rpc::config::ServerConfig;
32+
use katana_rpc_api::ApiKind;
33+
use tracing::Subscriber;
34+
use tracing_subscriber::{fmt, EnvFilter};
35+
use url::Url;
36+
37+
use crate::utils::{parse_genesis, parse_seed};
38+
39+
#[derive(Parser, Debug)]
40+
#[command(author, version, about, long_about = None)]
41+
#[command(propagate_version = true)]
42+
pub struct KatanaArgs {
43+
#[arg(long)]
44+
#[arg(help = "Don't print anything on startup.")]
45+
pub silent: bool,
46+
47+
#[arg(long)]
48+
#[arg(conflicts_with = "block_time")]
49+
#[arg(help = "Disable auto and interval mining, and mine on demand instead via an endpoint.")]
50+
pub no_mining: bool,
51+
52+
#[arg(short, long)]
53+
#[arg(value_name = "MILLISECONDS")]
54+
#[arg(help = "Block time in milliseconds for interval mining.")]
55+
pub block_time: Option<u64>,
56+
57+
#[arg(long)]
58+
#[arg(value_name = "PATH")]
59+
#[arg(help = "Directory path of the database to initialize from.")]
60+
#[arg(long_help = "Directory path of the database to initialize from. The path must either \
61+
be an empty directory or a directory which already contains a previously \
62+
initialized Katana database.")]
63+
pub db_dir: Option<PathBuf>,
64+
65+
#[arg(long)]
66+
#[arg(value_name = "URL")]
67+
#[arg(help = "The Starknet RPC provider to fork the network from.")]
68+
pub rpc_url: Option<Url>,
69+
70+
#[arg(long)]
71+
pub dev: bool,
72+
73+
#[arg(long)]
74+
#[arg(help = "Output logs in JSON format.")]
75+
pub json_log: bool,
76+
77+
/// Enable Prometheus metrics.
78+
///
79+
/// The metrics will be served at the given interface and port.
80+
#[arg(long, value_name = "SOCKET", value_parser = parse_socket_address, help_heading = "Metrics")]
81+
pub metrics: Option<SocketAddr>,
82+
83+
#[arg(long)]
84+
#[arg(requires = "rpc_url")]
85+
#[arg(value_name = "BLOCK_NUMBER")]
86+
#[arg(help = "Fork the network at a specific block.")]
87+
pub fork_block_number: Option<u64>,
88+
89+
#[cfg(feature = "messaging")]
90+
#[arg(long)]
91+
#[arg(value_name = "PATH")]
92+
#[arg(value_parser = katana_core::service::messaging::MessagingConfig::parse)]
93+
#[arg(help = "Configure the messaging with an other chain.")]
94+
#[arg(long_help = "Configure the messaging to allow Katana listening/sending messages on a \
95+
settlement chain that can be Ethereum or an other Starknet sequencer. \
96+
The configuration file details and examples can be found here: https://book.dojoengine.org/toolchain/katana/reference#messaging")]
97+
pub messaging: Option<katana_core::service::messaging::MessagingConfig>,
98+
99+
#[command(flatten)]
100+
#[command(next_help_heading = "Server options")]
101+
pub server: ServerOptions,
102+
103+
#[command(flatten)]
104+
#[command(next_help_heading = "Starknet options")]
105+
pub starknet: StarknetOptions,
106+
107+
#[command(subcommand)]
108+
pub command: Option<Commands>,
109+
}
110+
111+
#[derive(Debug, Subcommand)]
112+
pub enum Commands {
113+
#[command(about = "Generate shell completion file for specified shell")]
114+
Completions { shell: Shell },
115+
}
116+
117+
#[derive(Debug, Args, Clone)]
118+
pub struct ServerOptions {
119+
#[arg(short, long)]
120+
#[arg(default_value = "5050")]
121+
#[arg(help = "Port number to listen on.")]
122+
pub port: u16,
123+
124+
#[arg(long)]
125+
#[arg(help = "The IP address the server will listen on.")]
126+
pub host: Option<String>,
127+
128+
#[arg(long)]
129+
#[arg(default_value = "100")]
130+
#[arg(help = "Maximum number of concurrent connections allowed.")]
131+
pub max_connections: u32,
132+
133+
#[arg(long)]
134+
#[arg(value_delimiter = ',')]
135+
#[arg(help = "Enables the CORS layer and sets the allowed origins, separated by commas.")]
136+
pub allowed_origins: Option<Vec<String>>,
137+
}
138+
139+
#[derive(Debug, Args, Clone)]
140+
pub struct StarknetOptions {
141+
#[arg(long)]
142+
#[arg(default_value = "0")]
143+
#[arg(help = "Specify the seed for randomness of accounts to be predeployed.")]
144+
pub seed: String,
145+
146+
#[arg(long = "accounts")]
147+
#[arg(value_name = "NUM")]
148+
#[arg(default_value = "10")]
149+
#[arg(help = "Number of pre-funded accounts to generate.")]
150+
pub total_accounts: u16,
151+
152+
#[arg(long)]
153+
#[arg(help = "Disable charging fee when executing transactions.")]
154+
pub disable_fee: bool,
155+
156+
#[arg(long)]
157+
#[arg(help = "Disable validation when executing transactions.")]
158+
pub disable_validate: bool,
159+
160+
#[command(flatten)]
161+
#[command(next_help_heading = "Environment options")]
162+
pub environment: EnvironmentOptions,
163+
164+
#[arg(long)]
165+
#[arg(value_parser = parse_genesis)]
166+
#[arg(conflicts_with_all(["rpc_url", "seed", "total_accounts"]))]
167+
pub genesis: Option<Genesis>,
168+
}
169+
170+
#[derive(Debug, Args, Clone)]
171+
pub struct EnvironmentOptions {
172+
#[arg(long)]
173+
#[arg(help = "The chain ID.")]
174+
#[arg(long_help = "The chain ID. If a raw hex string (`0x` prefix) is provided, then it'd \
175+
used as the actual chain ID. Otherwise, it's represented as the raw \
176+
ASCII values. It must be a valid Cairo short string.")]
177+
#[arg(default_value = "KATANA")]
178+
#[arg(value_parser = ChainId::parse)]
179+
pub chain_id: ChainId,
180+
181+
#[arg(long)]
182+
#[arg(help = "The maximum number of steps available for the account validation logic.")]
183+
#[arg(default_value_t = DEFAULT_VALIDATE_MAX_STEPS)]
184+
pub validate_max_steps: u32,
185+
186+
#[arg(long)]
187+
#[arg(help = "The maximum number of steps available for the account execution logic.")]
188+
#[arg(default_value_t = DEFAULT_INVOKE_MAX_STEPS)]
189+
pub invoke_max_steps: u32,
190+
191+
#[arg(long = "eth-gas-price")]
192+
#[arg(conflicts_with = "genesis")]
193+
#[arg(help = "The L1 ETH gas price. (denominated in wei)")]
194+
#[arg(default_value_t = DEFAULT_ETH_L1_GAS_PRICE)]
195+
pub l1_eth_gas_price: u128,
196+
197+
#[arg(long = "strk-gas-price")]
198+
#[arg(conflicts_with = "genesis")]
199+
#[arg(help = "The L1 STRK gas price. (denominated in fri)")]
200+
#[arg(default_value_t = DEFAULT_STRK_L1_GAS_PRICE)]
201+
pub l1_strk_gas_price: u128,
202+
}
203+
204+
impl KatanaArgs {
205+
pub fn init_logging(&self) -> Result<(), Box<dyn std::error::Error>> {
206+
const DEFAULT_LOG_FILTER: &str = "info,executor=trace,forked_backend=trace,server=debug,\
207+
katana_core=trace,blockifier=off,jsonrpsee_server=off,\
208+
hyper=off,messaging=debug,node=error";
209+
210+
let builder = fmt::Subscriber::builder().with_env_filter(
211+
EnvFilter::try_from_default_env().or(EnvFilter::try_new(DEFAULT_LOG_FILTER))?,
212+
);
213+
214+
let subscriber: Box<dyn Subscriber + Send + Sync> = if self.json_log {
215+
Box::new(builder.json().finish())
216+
} else {
217+
Box::new(builder.finish())
218+
};
219+
220+
Ok(tracing::subscriber::set_global_default(subscriber)?)
221+
}
222+
223+
pub fn sequencer_config(&self) -> SequencerConfig {
224+
SequencerConfig {
225+
block_time: self.block_time,
226+
no_mining: self.no_mining,
227+
#[cfg(feature = "messaging")]
228+
messaging: self.messaging.clone(),
229+
}
230+
}
231+
232+
pub fn server_config(&self) -> ServerConfig {
233+
let mut apis = vec![ApiKind::Starknet, ApiKind::Katana, ApiKind::Torii, ApiKind::Saya];
234+
// only enable `katana` API in dev mode
235+
if self.dev {
236+
apis.push(ApiKind::Dev);
237+
}
238+
239+
ServerConfig {
240+
apis,
241+
port: self.server.port,
242+
host: self.server.host.clone().unwrap_or("0.0.0.0".into()),
243+
max_connections: self.server.max_connections,
244+
allowed_origins: self.server.allowed_origins.clone(),
245+
}
246+
}
247+
248+
pub fn starknet_config(&self) -> StarknetConfig {
249+
let genesis = match self.starknet.genesis.clone() {
250+
Some(genesis) => genesis,
251+
None => {
252+
let gas_prices = GasPrices {
253+
eth: self.starknet.environment.l1_eth_gas_price,
254+
strk: self.starknet.environment.l1_strk_gas_price,
255+
};
256+
257+
let accounts = DevAllocationsGenerator::new(self.starknet.total_accounts)
258+
.with_seed(parse_seed(&self.starknet.seed))
259+
.with_balance(U256::from(DEFAULT_PREFUNDED_ACCOUNT_BALANCE))
260+
.generate();
261+
262+
let mut genesis = Genesis {
263+
gas_prices,
264+
sequencer_address: *DEFAULT_SEQUENCER_ADDRESS,
265+
..Default::default()
266+
};
267+
268+
genesis.extend_allocations(accounts.into_iter().map(|(k, v)| (k, v.into())));
269+
genesis
270+
}
271+
};
272+
273+
StarknetConfig {
274+
disable_fee: self.starknet.disable_fee,
275+
disable_validate: self.starknet.disable_validate,
276+
fork_rpc_url: self.rpc_url.clone(),
277+
fork_block_number: self.fork_block_number,
278+
env: Environment {
279+
chain_id: self.starknet.environment.chain_id,
280+
invoke_max_steps: self.starknet.environment.invoke_max_steps,
281+
validate_max_steps: self.starknet.environment.validate_max_steps,
282+
},
283+
db_dir: self.db_dir.clone(),
284+
genesis,
285+
}
286+
}
287+
}
288+
289+
#[cfg(test)]
290+
mod test {
291+
use super::*;
292+
293+
#[test]
294+
fn test_starknet_config_default() {
295+
let args = KatanaArgs::parse_from(["katana"]);
296+
let config = args.starknet_config();
297+
298+
assert!(!config.disable_fee);
299+
assert!(!config.disable_validate);
300+
assert_eq!(config.fork_rpc_url, None);
301+
assert_eq!(config.fork_block_number, None);
302+
assert_eq!(config.env.chain_id, ChainId::parse("KATANA").unwrap());
303+
assert_eq!(config.env.invoke_max_steps, DEFAULT_INVOKE_MAX_STEPS);
304+
assert_eq!(config.env.validate_max_steps, DEFAULT_VALIDATE_MAX_STEPS);
305+
assert_eq!(config.db_dir, None);
306+
assert_eq!(config.genesis.gas_prices.eth, DEFAULT_ETH_L1_GAS_PRICE);
307+
assert_eq!(config.genesis.gas_prices.strk, DEFAULT_STRK_L1_GAS_PRICE);
308+
assert_eq!(config.genesis.sequencer_address, *DEFAULT_SEQUENCER_ADDRESS);
309+
}
310+
311+
#[test]
312+
fn test_starknet_config_custom() {
313+
let args = KatanaArgs::parse_from([
314+
"katana",
315+
"--disable-fee",
316+
"--disable-validate",
317+
"--chain-id",
318+
"SN_GOERLI",
319+
"--invoke-max-steps",
320+
"200",
321+
"--validate-max-steps",
322+
"100",
323+
"--db-dir",
324+
"/path/to/db",
325+
"--eth-gas-price",
326+
"10",
327+
"--strk-gas-price",
328+
"20",
329+
]);
330+
let config = args.starknet_config();
331+
332+
assert!(config.disable_fee);
333+
assert!(config.disable_validate);
334+
assert_eq!(config.env.chain_id, ChainId::GOERLI);
335+
assert_eq!(config.env.invoke_max_steps, 200);
336+
assert_eq!(config.env.validate_max_steps, 100);
337+
assert_eq!(config.db_dir, Some(PathBuf::from("/path/to/db")));
338+
assert_eq!(config.genesis.gas_prices.eth, 10);
339+
assert_eq!(config.genesis.gas_prices.strk, 20);
340+
}
341+
}

0 commit comments

Comments
 (0)