diff --git a/CHANGELOG.md b/CHANGELOG.md index 713bd1d41..4ea4ab93d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,12 @@ - feat: `exex_pragma_dispatch` implementation - feat: Madara ExExs proof of concept +- fix: override chain config +- fix: estimate_fee should through an error if any txn fails - fix: rejected transaction block production panic - fix(sync): pending block retrying mechanism +- feat(clean): dc_db: rename `DbBlockId::BlockN` to `DbBlockId::Number` +- feat(cli): Environment variables can be used to specify Madara parameters - fix:(tests): Add testing feature to mc-db dev dependency (#294) - feat: new crate gateway client & server - test: Starknet-js basic tests added diff --git a/Cargo.lock b/Cargo.lock index 584f9bd49..fe23907e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5374,14 +5374,12 @@ dependencies = [ "mp-rpc", "mp-transactions", "mp-utils", - "primitive-types", "rand", "rayon", "reqwest 0.12.5", "serde", "serde_json", "serde_yaml", - "starknet-core", "starknet-providers", "starknet-signers", "starknet_api", @@ -5428,7 +5426,6 @@ dependencies = [ "mp-receipt", "mp-state-update", "mp-transactions", - "mp-utils", "num-traits 0.2.19", "rayon", "rstest 0.18.2", @@ -5460,7 +5457,6 @@ dependencies = [ "mp-utils", "rayon", "rocksdb", - "rstest 0.18.2", "serde", "starknet-core", "starknet-types-core", @@ -5491,7 +5487,6 @@ dependencies = [ "mp-receipt", "mp-state-update", "mp-transactions", - "mp-utils", "proptest", "proptest-derive", "rand", @@ -5512,8 +5507,6 @@ dependencies = [ "env_logger 0.11.3", "flate2", "lazy_static", - "mp-utils", - "once_cell", "reqwest 0.12.5", "rstest 0.18.2", "serde_json", @@ -5679,7 +5672,6 @@ dependencies = [ "mp-rpc", "mp-state-update", "mp-transactions", - "mp-utils", "paste", "rstest 0.18.2", "serde_json", @@ -5709,18 +5701,13 @@ dependencies = [ "mp-convert", "mp-exex", "mp-gateway", - "mp-receipt", "mp-transactions", "mp-utils", - "num-traits 0.2.19", - "rand", - "rayon", "regex", "reqwest 0.12.5", "rstest 0.18.2", "serde_json", "starknet-core", - "starknet-providers", "starknet-types-core", "starknet_api", "tempfile", @@ -5842,10 +5829,8 @@ dependencies = [ "anyhow", "blockifier", "lazy_static", - "log", "mp-utils", "primitive-types", - "reqwest 0.12.5", "rstest 0.18.2", "serde", "serde_json", @@ -5859,7 +5844,6 @@ dependencies = [ name = "mp-class" version = "0.7.0" dependencies = [ - "anyhow", "blockifier", "cairo-lang-starknet 1.0.0-alpha.6", "cairo-lang-starknet 1.0.0-rc0", @@ -5876,7 +5860,6 @@ dependencies = [ "num-bigint", "serde", "serde_json", - "sha3", "starknet-core", "starknet-providers", "starknet-types-core", @@ -5946,7 +5929,6 @@ dependencies = [ "bincode 1.3.3", "blockifier", "cairo-vm", - "log", "mp-convert", "rstest 0.18.2", "serde", @@ -6026,6 +6008,7 @@ dependencies = [ "rayon", "rstest 0.18.2", "serde", + "serde_yaml", "tokio", "url", ] diff --git a/README.md b/README.md index 22ee5208d..21fd36b83 100644 --- a/README.md +++ b/README.md @@ -342,12 +342,18 @@ Toggle details for each namespace to view additional settings: ### Environment Variables -Set up your node's environment variables using the `STARKNET_` prefix. For example: +Set up your node's environment variables using the `MADARA_` prefix. For example: -- `STARKNET_BASE_PATH=/path/to/data` -- `STARKNET_LOG=info` +- `MADARA_BASE_PATH=/path/to/data` +- `MADARA_RPC_PORT=1111` -These variables allow you to adjust the node's configuration without using command-line arguments. +These variables allow you to adjust the node's configuration without using command-line arguments. If the command-line +argument is specified then it takes precedent over the environment variable. + +> [!CAUTION] +> Environment variables can be visible beyond the current process and are not +> encrypted. You should take special care when setting _secrets_ through +> environment variables, such as `MADARA_L1_ENDPOINT` or `MADARA_GATEWAY_KEY` ### Configuration File diff --git a/configs/chain_config.example.yaml b/configs/chain_config.example.yaml index 1c40f0524..b21df8cab 100644 --- a/configs/chain_config.example.yaml +++ b/configs/chain_config.example.yaml @@ -10,7 +10,7 @@ native_fee_token_address: "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201 parent_fee_token_address: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7" # Paths to JSON files containing blockifier's constants for different versions -versioned_constants: +versioned_constants_path: "0.13.0": "crates/primitives/chain_config/resources/versioned_constants_13_0.json" "0.13.1": "crates/primitives/chain_config/resources/versioned_constants_13_1.json" "0.13.1.1": "crates/primitives/chain_config/resources/versioned_constants_13_1_1.json" diff --git a/configs/presets/devnet.yaml b/configs/presets/devnet.yaml index 71fce1d64..620e75dc4 100644 --- a/configs/presets/devnet.yaml +++ b/configs/presets/devnet.yaml @@ -2,11 +2,6 @@ chain_name: "Madara" chain_id: "MADARA_DEVNET" native_fee_token_address: "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d" parent_fee_token_address: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7" -versioned_constants: - "0.13.0": "crates/primitives/chain_config/resources/versioned_constants_13_0.json" - "0.13.1": "crates/primitives/chain_config/resources/versioned_constants_13_1.json" - "0.13.1.1": "crates/primitives/chain_config/resources/versioned_constants_13_1_1.json" - "0.13.2": "crates/primitives/chain_config/resources/versioned_constants_13_2.json" eth_core_contract_address: "0xE2Bb56ee936fd6433DC0F6e7e3b8365C906AA057" latest_protocol_version: "0.13.2" block_time: "1s" diff --git a/configs/presets/integration.yaml b/configs/presets/integration.yaml index 2b522fd3c..eb071072a 100644 --- a/configs/presets/integration.yaml +++ b/configs/presets/integration.yaml @@ -2,11 +2,6 @@ chain_name: "Starknet Sepolia" chain_id: "SN_INTEGRATION_SEPOLIA" native_fee_token_address: "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d" parent_fee_token_address: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7" -versioned_constants: - "0.13.0": "crates/primitives/chain_config/resources/versioned_constants_13_0.json" - "0.13.1": "crates/primitives/chain_config/resources/versioned_constants_13_1.json" - "0.13.1.1": "crates/primitives/chain_config/resources/versioned_constants_13_1_1.json" - "0.13.2": "crates/primitives/chain_config/resources/versioned_constants_13_2.json" eth_core_contract_address: "0x4737c0c1B4D5b1A687B42610DdabEE781152359c" latest_protocol_version: "0.13.2" block_time: "30s" @@ -30,5 +25,5 @@ bouncer_config: message_segment_length: 18446744073709551615 n_events: 18446744073709551615 state_diff_size: 131072 -sequencer_address: "0x0" +sequencer_address: "0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8" max_nonce_for_validation_skip: 2 diff --git a/configs/presets/mainnet.yaml b/configs/presets/mainnet.yaml index 305f60df7..5c24b7f3c 100644 --- a/configs/presets/mainnet.yaml +++ b/configs/presets/mainnet.yaml @@ -2,11 +2,6 @@ chain_name: "Starknet Mainnet" chain_id: "SN_MAIN" native_fee_token_address: "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d" parent_fee_token_address: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7" -versioned_constants: - "0.13.0": "crates/primitives/chain_config/resources/versioned_constants_13_0.json" - "0.13.1": "crates/primitives/chain_config/resources/versioned_constants_13_1.json" - "0.13.1.1": "crates/primitives/chain_config/resources/versioned_constants_13_1_1.json" - "0.13.2": "crates/primitives/chain_config/resources/versioned_constants_13_2.json" eth_core_contract_address: "0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4" latest_protocol_version: "0.13.2" block_time: "30s" @@ -30,5 +25,5 @@ bouncer_config: message_segment_length: 18446744073709551615 n_events: 18446744073709551615 state_diff_size: 131072 -sequencer_address: "0x0" +sequencer_address: "0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8" max_nonce_for_validation_skip: 2 diff --git a/configs/presets/sepolia.yaml b/configs/presets/sepolia.yaml index 3186427e3..868a5e36e 100644 --- a/configs/presets/sepolia.yaml +++ b/configs/presets/sepolia.yaml @@ -2,11 +2,6 @@ chain_name: "Starknet Sepolia" chain_id: "SN_SEPOLIA" native_fee_token_address: "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d" parent_fee_token_address: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7" -versioned_constants: - "0.13.0": "crates/primitives/chain_config/resources/versioned_constants_13_0.json" - "0.13.1": "crates/primitives/chain_config/resources/versioned_constants_13_1.json" - "0.13.1.1": "crates/primitives/chain_config/resources/versioned_constants_13_1_1.json" - "0.13.2": "crates/primitives/chain_config/resources/versioned_constants_13_2.json" eth_core_contract_address: "0xE2Bb56ee936fd6433DC0F6e7e3b8365C906AA057" latest_protocol_version: "0.13.2" block_time: "30s" @@ -30,5 +25,5 @@ bouncer_config: message_segment_length: 18446744073709551615 n_events: 18446744073709551615 state_diff_size: 131072 -sequencer_address: "0x0" +sequencer_address: "0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8" max_nonce_for_validation_skip: 2 diff --git a/crates/client/block_import/Cargo.toml b/crates/client/block_import/Cargo.toml index b9e1481d1..c827e619a 100644 --- a/crates/client/block_import/Cargo.toml +++ b/crates/client/block_import/Cargo.toml @@ -31,7 +31,6 @@ mp-convert.workspace = true mp-receipt.workspace = true mp-state-update.workspace = true mp-transactions.workspace = true -mp-utils.workspace = true bonsai-trie.workspace = true starknet-core.workspace = true diff --git a/crates/client/db/Cargo.toml b/crates/client/db/Cargo.toml index 0fe66a000..95438cfef 100644 --- a/crates/client/db/Cargo.toml +++ b/crates/client/db/Cargo.toml @@ -35,7 +35,6 @@ bincode = { workspace = true } log = { workspace = true, default-features = true } rayon = { workspace = true } rocksdb.workspace = true -rstest = { workspace = true } serde = { workspace = true } tempfile = { workspace = true, optional = true } thiserror = { workspace = true } diff --git a/crates/client/db/src/block_db.rs b/crates/client/db/src/block_db.rs index 30e542d14..37a5db49a 100644 --- a/crates/client/db/src/block_db.rs +++ b/crates/client/db/src/block_db.rs @@ -264,9 +264,9 @@ impl MadaraBackend { pub(crate) fn id_to_storage_type(&self, id: &BlockId) -> Result> { match id { - BlockId::Hash(hash) => Ok(self.block_hash_to_block_n(hash)?.map(DbBlockId::BlockN)), - BlockId::Number(block_n) => Ok(Some(DbBlockId::BlockN(*block_n))), - BlockId::Tag(BlockTag::Latest) => Ok(self.get_latest_block_n()?.map(DbBlockId::BlockN)), + BlockId::Hash(hash) => Ok(self.block_hash_to_block_n(hash)?.map(DbBlockId::Number)), + BlockId::Number(block_n) => Ok(Some(DbBlockId::Number(*block_n))), + BlockId::Tag(BlockTag::Latest) => Ok(self.get_latest_block_n()?.map(DbBlockId::Number)), BlockId::Tag(BlockTag::Pending) => Ok(Some(DbBlockId::Pending)), } } @@ -274,7 +274,7 @@ impl MadaraBackend { fn storage_to_info(&self, id: &DbBlockId) -> Result> { match id { DbBlockId::Pending => Ok(Some(MadaraMaybePendingBlockInfo::Pending(self.get_pending_block_info()?))), - DbBlockId::BlockN(block_n) => { + DbBlockId::Number(block_n) => { Ok(self.get_block_info_from_block_n(*block_n)?.map(MadaraMaybePendingBlockInfo::NotPending)) } } @@ -283,7 +283,7 @@ impl MadaraBackend { fn storage_to_inner(&self, id: &DbBlockId) -> Result> { match id { DbBlockId::Pending => Ok(Some(self.get_pending_block_inner()?)), - DbBlockId::BlockN(block_n) => self.get_block_inner_from_block_n(*block_n), + DbBlockId::Number(block_n) => self.get_block_inner_from_block_n(*block_n), } } @@ -292,7 +292,7 @@ impl MadaraBackend { pub fn get_block_n(&self, id: &impl DbBlockIdResolvable) -> Result> { let Some(ty) = id.resolve_db_block_id(self)? else { return Ok(None) }; match &ty { - DbBlockId::BlockN(block_id) => Ok(Some(*block_id)), + DbBlockId::Number(block_id) => Ok(Some(*block_id)), DbBlockId::Pending => Ok(None), } } @@ -301,7 +301,7 @@ impl MadaraBackend { let Some(ty) = id.resolve_db_block_id(self)? else { return Ok(None) }; match &ty { // TODO: fast path if id is already a block hash.. - DbBlockId::BlockN(block_n) => Ok(self.get_block_info_from_block_n(*block_n)?.map(|b| b.block_hash)), + DbBlockId::Number(block_n) => Ok(self.get_block_info_from_block_n(*block_n)?.map(|b| b.block_hash)), DbBlockId::Pending => Ok(None), } } @@ -310,7 +310,7 @@ impl MadaraBackend { let Some(ty) = id.resolve_db_block_id(self)? else { return Ok(None) }; match ty { DbBlockId::Pending => Ok(Some(self.get_pending_block_state_update()?)), - DbBlockId::BlockN(block_n) => self.get_state_update(block_n), + DbBlockId::Number(block_n) => self.get_state_update(block_n), } } diff --git a/crates/client/db/src/class_db.rs b/crates/client/db/src/class_db.rs index c1c285064..aa57efd80 100644 --- a/crates/client/db/src/class_db.rs +++ b/crates/client/db/src/class_db.rs @@ -67,7 +67,7 @@ impl MadaraBackend { let valid = match (requested_id, info.block_id) { (DbBlockId::Pending, _) => true, - (DbBlockId::BlockN(block_n), DbBlockId::BlockN(real_block_n)) => real_block_n <= block_n, + (DbBlockId::Number(block_n), DbBlockId::Number(real_block_n)) => real_block_n <= block_n, _ => false, }; if !valid { @@ -174,7 +174,7 @@ impl MadaraBackend { block_number: u64, converted_classes: &[ConvertedClass], ) -> Result<(), MadaraStorageError> { - self.store_classes(DbBlockId::BlockN(block_number), converted_classes, Column::ClassInfo, Column::ClassCompiled) + self.store_classes(DbBlockId::Number(block_number), converted_classes, Column::ClassInfo, Column::ClassCompiled) } /// NB: This functions needs to run on the rayon thread pool diff --git a/crates/client/db/src/contract_db.rs b/crates/client/db/src/contract_db.rs index 7c6069516..fa3f53724 100644 --- a/crates/client/db/src/contract_db.rs +++ b/crates/client/db/src/contract_db.rs @@ -51,7 +51,7 @@ impl MadaraBackend { let Some(block_n) = self.get_latest_block_n()? else { return Ok(None) }; block_n } - DbBlockId::BlockN(block_n) => block_n, + DbBlockId::Number(block_n) => block_n, }; // We try to find history values. diff --git a/crates/client/db/src/db_block_id.rs b/crates/client/db/src/db_block_id.rs index 4f75c5c9c..75471719b 100644 --- a/crates/client/db/src/db_block_id.rs +++ b/crates/client/db/src/db_block_id.rs @@ -7,7 +7,7 @@ use crate::{MadaraBackend, MadaraStorageError}; #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub enum DbBlockId { Pending, - BlockN(u64), + Number(u64), } impl DbBlockId { @@ -42,7 +42,7 @@ impl fmt::Display for DbBlockId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Pending => write!(f, "#"), - Self::BlockN(block_n) => write!(f, "#{block_n}"), + Self::Number(block_n) => write!(f, "#{block_n}"), } } } @@ -60,6 +60,6 @@ mod test { #[test] fn test_db_block_id() { assert!(DbBlockId::Pending.is_pending()); - assert!(!DbBlockId::BlockN(0).is_pending()); + assert!(!DbBlockId::Number(0).is_pending()); } } diff --git a/crates/client/db/src/tests/test_block.rs b/crates/client/db/src/tests/test_block.rs index 810789721..f9531b401 100644 --- a/crates/client/db/src/tests/test_block.rs +++ b/crates/client/db/src/tests/test_block.rs @@ -28,8 +28,8 @@ mod block_tests { backend.store_block(block.clone(), state_diff.clone(), vec![]).unwrap(); backend.store_block(pending_block_one(), pending_state_diff_one(), vec![]).unwrap(); - assert_eq!(backend.resolve_block_id(&BlockId::Hash(block_hash)).unwrap().unwrap(), DbBlockId::BlockN(0)); - assert_eq!(backend.resolve_block_id(&BlockId::Number(0)).unwrap().unwrap(), DbBlockId::BlockN(0)); + assert_eq!(backend.resolve_block_id(&BlockId::Hash(block_hash)).unwrap().unwrap(), DbBlockId::Number(0)); + assert_eq!(backend.resolve_block_id(&BlockId::Number(0)).unwrap().unwrap(), DbBlockId::Number(0)); assert_eq!(backend.resolve_block_id(&DbBlockId::Pending).unwrap().unwrap(), DbBlockId::Pending); } @@ -43,7 +43,7 @@ mod block_tests { #[tokio::test] async fn test_store_block() { - const BLOCK_ID_0: DbBlockId = DbBlockId::BlockN(0); + const BLOCK_ID_0: DbBlockId = DbBlockId::Number(0); let db = temp_db().await; let backend = db.backend(); diff --git a/crates/client/devnet/Cargo.toml b/crates/client/devnet/Cargo.toml index e8185e9a8..5d9ef7693 100644 --- a/crates/client/devnet/Cargo.toml +++ b/crates/client/devnet/Cargo.toml @@ -37,7 +37,6 @@ mp-convert.workspace = true mp-receipt.workspace = true mp-state-update.workspace = true mp-transactions.workspace = true -mp-utils.workspace = true # Starknet blockifier.workspace = true diff --git a/crates/client/exec/src/block_context.rs b/crates/client/exec/src/block_context.rs index 51c4a5c33..8ba86fc3f 100644 --- a/crates/client/exec/src/block_context.rs +++ b/crates/client/exec/src/block_context.rs @@ -35,9 +35,9 @@ impl ExecutionContext { pub fn init_cached_state(&self) -> CachedState { let on_top_of = match self.db_id { DbBlockId::Pending => Some(DbBlockId::Pending), - DbBlockId::BlockN(block_n) => { + DbBlockId::Number(block_n) => { // We exec on top of the previous block. None means we are executing genesis. - block_n.checked_sub(1).map(DbBlockId::BlockN) + block_n.checked_sub(1).map(DbBlockId::Number) } }; @@ -70,7 +70,7 @@ impl ExecutionContext { block.header.l1_da_mode, ), MadaraMaybePendingBlockInfo::NotPending(block) => ( - DbBlockId::BlockN(block.header.block_number), + DbBlockId::Number(block.header.block_number), block.header.protocol_version, block.header.block_number, block.header.block_timestamp, diff --git a/crates/client/rpc/Cargo.toml b/crates/client/rpc/Cargo.toml index 4882d0608..70b1321db 100644 --- a/crates/client/rpc/Cargo.toml +++ b/crates/client/rpc/Cargo.toml @@ -32,7 +32,6 @@ mp-receipt = { workspace = true } mp-rpc = { workspace = true } mp-state-update = { workspace = true } mp-transactions = { workspace = true } -mp-utils = { workspace = true } # Starknet blockifier = { workspace = true, default-features = true } diff --git a/crates/client/rpc/src/versions/v0_7_1/methods/read/estimate_fee.rs b/crates/client/rpc/src/versions/v0_7_1/methods/read/estimate_fee.rs index cc35af2b5..20d440101 100644 --- a/crates/client/rpc/src/versions/v0_7_1/methods/read/estimate_fee.rs +++ b/crates/client/rpc/src/versions/v0_7_1/methods/read/estimate_fee.rs @@ -46,8 +46,19 @@ pub async fn estimate_fee( let execution_results = exec_context.re_execute_transactions([], transactions, true, validate)?; - let fee_estimates = - execution_results.iter().map(|result| exec_context.execution_result_to_fee_estimate(result)).collect(); + let fee_estimates = execution_results.iter().enumerate().try_fold( + Vec::with_capacity(execution_results.len()), + |mut acc, (index, result)| { + if result.execution_info.is_reverted() { + return Err(StarknetRpcApiError::TxnExecutionError { + tx_index: index, + error: result.execution_info.revert_error.clone().unwrap_or_default(), + }); + } + acc.push(exec_context.execution_result_to_fee_estimate(result)); + Ok(acc) + }, + )?; Ok(fee_estimates) } diff --git a/crates/client/rpc/src/versions/v0_7_1/methods/read/get_state_update.rs b/crates/client/rpc/src/versions/v0_7_1/methods/read/get_state_update.rs index f89e47106..a3e6d6333 100644 --- a/crates/client/rpc/src/versions/v0_7_1/methods/read/get_state_update.rs +++ b/crates/client/rpc/src/versions/v0_7_1/methods/read/get_state_update.rs @@ -61,7 +61,7 @@ pub fn get_state_update(starknet: &Starknet, block_id: BlockId) -> StarknetRpcRe // Get the old root from the previous block if it exists, otherwise default to zero. let old_root = if let Some(val) = block_info.header.block_number.checked_sub(1) { - let prev_block_info = &starknet.get_block_info(&DbBlockId::BlockN(val))?; + let prev_block_info = &starknet.get_block_info(&DbBlockId::Number(val))?; let prev_block_info = prev_block_info.as_nonpending().ok_or_internal_server_error("Block should not be pending")?; diff --git a/crates/client/sync/Cargo.toml b/crates/client/sync/Cargo.toml index 8bb663ece..a4325bc3d 100644 --- a/crates/client/sync/Cargo.toml +++ b/crates/client/sync/Cargo.toml @@ -29,13 +29,11 @@ mp-class.workspace = true mp-convert.workspace = true mp-exex.workspace = true mp-gateway.workspace = true -mp-receipt.workspace = true mp-transactions.workspace = true mp-utils.workspace = true # Starknet starknet-core.workspace = true -starknet-providers.workspace = true starknet-types-core.workspace = true starknet_api.workspace = true @@ -44,9 +42,6 @@ starknet_api.workspace = true anyhow.workspace = true futures = { workspace = true, default-features = true } log.workspace = true -num-traits.workspace = true -rand.workspace = true -rayon.workspace = true reqwest.workspace = true serde_json.workspace = true thiserror.workspace = true diff --git a/crates/client/sync/src/l2.rs b/crates/client/sync/src/l2.rs index 60bccee3e..7222b52fc 100644 --- a/crates/client/sync/src/l2.rs +++ b/crates/client/sync/src/l2.rs @@ -344,7 +344,7 @@ mod tests { Err(_) => panic!("Timeout reached while waiting for task completion"), } - let applied_block = backend.get_block(&DbBlockId::BlockN(0)).unwrap(); + let applied_block = backend.get_block(&DbBlockId::Number(0)).unwrap(); assert!(applied_block.is_some(), "The block was not applied correctly"); let applied_block = MadaraBlock::try_from(applied_block.unwrap()).unwrap(); diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index e32273bdd..b9709c4c0 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -38,7 +38,6 @@ mp-utils = { workspace = true } # Starknet blockifier = { workspace = true } -starknet-core = { workspace = true } starknet-providers = { workspace = true } starknet_api = { workspace = true } @@ -47,7 +46,7 @@ alloy = { workspace = true } anyhow.workspace = true async-trait = { workspace = true } chrono = "0.4.38" -clap = { workspace = true, features = ["derive"] } +clap = { workspace = true, features = ["derive", "env"] } env_logger.workspace = true fdlimit.workspace = true forwarded-header-value = "0.1.1" @@ -58,7 +57,6 @@ ip_network.workspace = true jsonrpsee.workspace = true lazy_static = { workspace = true } log = { workspace = true } -primitive-types.workspace = true rand = { workspace = true } rayon.workspace = true reqwest = { workspace = true } diff --git a/crates/node/src/cli/block_production.rs b/crates/node/src/cli/block_production.rs index 9abe2bd2a..e85d18a10 100644 --- a/crates/node/src/cli/block_production.rs +++ b/crates/node/src/cli/block_production.rs @@ -3,15 +3,15 @@ pub struct BlockProductionParams { /// Disable the block production service. /// The block production service is only enabled with the authority (sequencer) mode. - #[arg(long, alias = "no-block-production")] + #[arg(env = "MADARA_BLOCK_PRODUCTION_DISABLED", long, alias = "no-block-production")] pub block_production_disabled: bool, - /// Launch a devnet with a producton chain id (like SN_MAINNET, SN_SEPOLIA). - /// This in unsafe because your devnet transactiosn can be replayed on the actual network. - #[arg(long, default_value_t = false)] + /// Launch a devnet with a production chain id (like SN_MAINNET, SN_SEPOLIA). + /// This in unsafe because your devnet transactions can be replayed on the actual network. + #[arg(env = "MADARA_OVERRIDE_DEVNET_CHAIN_ID", long, default_value_t = false)] pub override_devnet_chain_id: bool, /// Create this number of contracts in the genesis block for the devnet configuration. - #[arg(long, default_value_t = 10)] + #[arg(env = "MADARA_DEVNET_CONTRACTS", long, default_value_t = 10)] pub devnet_contracts: u64, } diff --git a/crates/node/src/cli/chain_config_overrides.rs b/crates/node/src/cli/chain_config_overrides.rs index f16633ebe..6e5f3de20 100644 --- a/crates/node/src/cli/chain_config_overrides.rs +++ b/crates/node/src/cli/chain_config_overrides.rs @@ -1,73 +1,51 @@ use std::time::Duration; -use anyhow::Context; +use anyhow::{bail, Context}; +use blockifier::bouncer::BouncerConfig; +use clap::Parser; use serde::{Deserialize, Serialize}; use serde_yaml::Value; use starknet_api::core::{ChainId, ContractAddress}; use mp_block::H160; -use mp_chain_config::{ChainConfig, StarknetVersion}; -use mp_utils::parsers::parse_key_value; +use mp_chain_config::{ + deserialize_bouncer_config, deserialize_starknet_version, serialize_bouncer_config, serialize_starknet_version, + ChainConfig, StarknetVersion, +}; +use mp_utils::parsers::parse_key_value_yaml; +use mp_utils::serde::{deserialize_duration, serialize_duration}; /// Override chain config parameters. -/// Format: "--chain-config-override chain_id=NEW_MADARA --chain-config-override chain_name=NEW_NAME" -#[derive(clap::Parser, Clone, Debug)] +/// Format: "--chain-config-override chain_id=SN_MADARA,chain_name=MADARA,block_time=1500ms,bouncer_config.block_max_capacity.n_steps=100000000" +#[derive(Parser, Clone, Debug)] pub struct ChainConfigOverrideParams { - #[clap(long = "chain-config-override", value_parser = parse_key_value, number_of_values = 1)] - pub overrides: Vec<(String, String)>, + #[clap(env = "MADARA_CHAIN_CONFIG_OVERRIDE", long = "chain-config-override", value_parser = parse_key_value_yaml, use_value_delimiter = true, value_delimiter = ',')] + pub overrides: Vec<(String, Value)>, } -impl ChainConfigOverrideParams { - pub fn override_chain_config(&self, chain_config: ChainConfig) -> anyhow::Result { - let overridable = OverridableChainConfig::from(&chain_config); - let mut config_value = - serde_yaml::to_value(overridable).context("Failed to convert OverridableChainConfig to Value")?; - - if let Value::Mapping(ref mut map) = config_value { - for (key, value) in &self.overrides { - if !map.contains_key(Value::String(key.clone())) { - return Err(anyhow::anyhow!("The field '{}' is not overridable for the Chain Config.", key)); - } - map.insert(Value::String(key.clone()), Value::String(value.clone())); - } - } else { - return Err(anyhow::anyhow!("Unexpected chain config structure.")); - } - - let updated_overridable: OverridableChainConfig = - serde_yaml::from_value(config_value).context("Failed to convert Value back to OverridableChainConfig")?; - - Ok(ChainConfig { - versioned_constants: chain_config.versioned_constants, - bouncer_config: chain_config.bouncer_config, - ..updated_overridable.into() - }) - } -} - -/// Part of the Chain Config that we can override. -// We need this proxy structure to implement Serialize - -// which is not possible on the original ChainConfig because the bouncer config and -// the versioned constants don't implement it. -// Since we don't want to override those values anyway, we can create this wrapper. #[derive(Debug, Serialize, Deserialize)] -pub struct OverridableChainConfig { +pub struct ChainConfigOverridesInner { pub chain_name: String, pub chain_id: ChainId, pub native_fee_token_address: ContractAddress, pub parent_fee_token_address: ContractAddress, + #[serde(deserialize_with = "deserialize_starknet_version", serialize_with = "serialize_starknet_version")] pub latest_protocol_version: StarknetVersion, + #[serde(deserialize_with = "deserialize_duration", serialize_with = "serialize_duration")] pub block_time: Duration, + #[serde(deserialize_with = "deserialize_duration", serialize_with = "serialize_duration")] pub pending_block_update_time: Duration, pub execution_batch_size: usize, + #[serde(deserialize_with = "deserialize_bouncer_config", serialize_with = "serialize_bouncer_config")] + pub bouncer_config: BouncerConfig, pub sequencer_address: ContractAddress, pub max_nonce_for_validation_skip: u64, pub eth_core_contract_address: H160, } -impl From<&ChainConfig> for OverridableChainConfig { +impl From<&ChainConfig> for ChainConfigOverridesInner { fn from(config: &ChainConfig) -> Self { - OverridableChainConfig { + Self { chain_name: config.chain_name.clone(), chain_id: config.chain_id.clone(), native_fee_token_address: config.native_fee_token_address, @@ -76,6 +54,7 @@ impl From<&ChainConfig> for OverridableChainConfig { block_time: config.block_time, pending_block_update_time: config.pending_block_update_time, execution_batch_size: config.execution_batch_size, + bouncer_config: config.bouncer_config.clone(), sequencer_address: config.sequencer_address, max_nonce_for_validation_skip: config.max_nonce_for_validation_skip, eth_core_contract_address: config.eth_core_contract_address, @@ -83,22 +62,58 @@ impl From<&ChainConfig> for OverridableChainConfig { } } -impl From for ChainConfig { - fn from(overridable: OverridableChainConfig) -> Self { - ChainConfig { - chain_name: overridable.chain_name, - chain_id: overridable.chain_id, - native_fee_token_address: overridable.native_fee_token_address, - parent_fee_token_address: overridable.parent_fee_token_address, - latest_protocol_version: overridable.latest_protocol_version, - block_time: overridable.block_time, - pending_block_update_time: overridable.pending_block_update_time, - execution_batch_size: overridable.execution_batch_size, - sequencer_address: overridable.sequencer_address, - max_nonce_for_validation_skip: overridable.max_nonce_for_validation_skip, - eth_core_contract_address: overridable.eth_core_contract_address, - versioned_constants: Default::default(), - bouncer_config: Default::default(), +impl ChainConfigOverrideParams { + pub fn override_chain_config(&self, chain_config: ChainConfig) -> anyhow::Result { + let mut chain_config_overrides = serde_yaml::to_value(ChainConfigOverridesInner::from(&chain_config)) + .context("Failed to convert ChainConfig to Value")?; + + for (key, value) in &self.overrides { + // Split the key by '.' to handle nested fields + let key_parts = key.split('.').collect::>(); + + // Navigate to the last field in the path + let mut current_value = &mut chain_config_overrides; + for part in key_parts.iter().take(key_parts.len() - 1) { + current_value = match current_value.get_mut(part) { + Some(v) => v, + None => bail!("Invalid chain config override key path: {}", key), + }; + } + + // Set the value to the final field in the path + let last_key = + key_parts.last().with_context(|| format!("Invalid chain config override key path: {}", key))?; + match current_value.get_mut(*last_key) { + Some(field) => { + *field = value.clone(); + } + None => { + bail!("Invalid chain config override key path: {}", key); + } + } } + + let chain_config_overrides: ChainConfigOverridesInner = serde_yaml::from_value(chain_config_overrides) + .context("Failed to convert Value to ChainConfigOverridesInner")?; + + println!("chain_config_overrides: {:#?}", chain_config_overrides); + + let versioned_constants = chain_config.versioned_constants; + + Ok(ChainConfig { + chain_name: chain_config_overrides.chain_name, + chain_id: chain_config_overrides.chain_id, + native_fee_token_address: chain_config_overrides.native_fee_token_address, + parent_fee_token_address: chain_config_overrides.parent_fee_token_address, + latest_protocol_version: chain_config_overrides.latest_protocol_version, + block_time: chain_config_overrides.block_time, + pending_block_update_time: chain_config_overrides.pending_block_update_time, + execution_batch_size: chain_config_overrides.execution_batch_size, + bouncer_config: chain_config_overrides.bouncer_config, + sequencer_address: chain_config_overrides.sequencer_address, + max_nonce_for_validation_skip: chain_config_overrides.max_nonce_for_validation_skip, + eth_core_contract_address: chain_config_overrides.eth_core_contract_address, + versioned_constants, + }) } } diff --git a/crates/node/src/cli/db.rs b/crates/node/src/cli/db.rs index db2e86451..59d0f0743 100644 --- a/crates/node/src/cli/db.rs +++ b/crates/node/src/cli/db.rs @@ -3,14 +3,14 @@ use std::path::PathBuf; #[derive(Clone, Debug, clap::Args)] pub struct DbParams { /// The path where madara will store the database. You should probably change it. - #[clap(long, default_value = "/tmp/madara", value_name = "PATH")] + #[clap(env = "MADARA_BASE_PATH", long, default_value = "/tmp/madara", value_name = "PATH")] pub base_path: PathBuf, /// Directory for backups. Use it with `--restore-from-latest-backup` or `--backup-every-n-blocks `. - #[clap(long, value_name = "PATH")] + #[clap(env = "MADARA_BACKUP_DIR", long, value_name = "PATH")] pub backup_dir: Option, /// Restore the database at startup from the latest backup version. Use it with `--backup-dir ` - #[clap(long)] + #[clap(env = "MADARA_RESTORE_FROM_LATEST_BACKUP", long)] pub restore_from_latest_backup: bool, } diff --git a/crates/node/src/cli/gateway.rs b/crates/node/src/cli/gateway.rs index 5a14521d5..baeccdaf8 100644 --- a/crates/node/src/cli/gateway.rs +++ b/crates/node/src/cli/gateway.rs @@ -4,18 +4,18 @@ use clap::Args; #[derive(Debug, Clone, Args)] pub struct GatewayParams { /// Enable the feeder gateway server. - #[arg(long, alias = "feeder-gateway")] + #[arg(env = "MADARA_FEEDER_GATEWAY_ENABLE", long, alias = "feeder-gateway")] pub feeder_gateway_enable: bool, /// Enable the gateway server. - #[arg(long, alias = "gateway")] + #[arg(env = "MADARA_GATEWAY_ENABLE", long, alias = "gateway")] pub gateway_enable: bool, /// Listen on all network interfaces. This usually means the gateway server will be accessible externally. - #[arg(long)] + #[arg(env = "MADARA_GATEWAY_EXTERNAL", long)] pub gateway_external: bool, /// The gateway port to listen at. - #[arg(long, value_name = "GATEWAY PORT", default_value = "8080")] + #[arg(env = "MADARA_GATEWAY_PORT", long, value_name = "GATEWAY PORT", default_value = "8080")] pub gateway_port: u16, } diff --git a/crates/node/src/cli/l1.rs b/crates/node/src/cli/l1.rs index 1c3fcd750..9eaed8439 100644 --- a/crates/node/src/cli/l1.rs +++ b/crates/node/src/cli/l1.rs @@ -7,19 +7,20 @@ use mp_utils::parsers::{parse_duration, parse_url}; #[derive(Clone, Debug, clap::Args)] pub struct L1SyncParams { /// Disable L1 sync. - #[clap(long, alias = "no-l1-sync", conflicts_with = "l1_endpoint")] + #[clap(env = "MADARA_SYNC_L1_DISABLED", long, alias = "no-l1-sync", conflicts_with = "l1_endpoint")] pub sync_l1_disabled: bool, /// The L1 rpc endpoint url for state verification. - #[clap(long, value_parser = parse_url, value_name = "ETHEREUM RPC URL")] + #[clap(env = "MADARA_L1_ENDPOINT", long, value_parser = parse_url, value_name = "ETHEREUM RPC URL")] pub l1_endpoint: Option, /// Disable the gas price sync service. The sync service is responsible to fetch the fee history from the ethereum. - #[clap(long, alias = "no-gas-price-sync")] + #[clap(env = "MADARA_GAS_PRICE_SYNC_DISABLED", long, alias = "no-gas-price-sync")] pub gas_price_sync_disabled: bool, /// Time in which the gas price worker will fetch the gas price. #[clap( + env = "MADARA_GAS_PRICE_POLL", long, default_value = "10s", value_parser = parse_duration, diff --git a/crates/node/src/cli/mod.rs b/crates/node/src/cli/mod.rs index 923787453..28f311b75 100644 --- a/crates/node/src/cli/mod.rs +++ b/crates/node/src/cli/mod.rs @@ -50,7 +50,7 @@ use url::Url; pub struct RunCmd { /// The human-readable name for this node. /// It is used as the network node name. - #[arg(long, value_name = "NAME")] + #[arg(env = "MADARA_NAME", long, value_name = "NAME")] pub name: Option, #[allow(missing_docs)] @@ -86,27 +86,27 @@ pub struct RunCmd { pub block_production_params: BlockProductionParams, /// The node will run as a sequencer and produce its own state. - #[arg(long, group = "mode")] + #[arg(env = "MADARA_SEQUENCER", long, group = "mode")] pub sequencer: bool, /// The node will run as a full node and sync the state of a specific network from others. - #[arg(long, group = "mode")] + #[arg(env = "MADARA_FULL", long, group = "mode")] pub full: bool, /// The node will run as a testing sequencer with predeployed contracts. - #[arg(long, group = "mode")] + #[arg(env = "MADARA_DEVNET", long, group = "mode")] pub devnet: bool, /// The network chain configuration. - #[clap(long, short, group = "full_mode_config")] + #[clap(env = "MADARA_NETWORK", long, short, group = "full_mode_config")] pub network: Option, /// Chain configuration file path. - #[clap(long, value_name = "CHAIN CONFIG FILE PATH", group = "chain_config")] + #[clap(env = "MADARA_CHAIN_CONFIG_PATH", long, value_name = "CHAIN CONFIG FILE PATH", group = "chain_config")] pub chain_config_path: Option, /// Use preset as chain Config - #[clap(long, value_name = "PRESET NAME", group = "chain_config")] + #[clap(env = "MADARA_PRESET", long, value_name = "PRESET NAME", group = "chain_config")] pub preset: Option, /// Overrides parameters from the Chain Config. diff --git a/crates/node/src/cli/prometheus.rs b/crates/node/src/cli/prometheus.rs index 0c350059c..12b11e0f6 100644 --- a/crates/node/src/cli/prometheus.rs +++ b/crates/node/src/cli/prometheus.rs @@ -4,12 +4,12 @@ use clap::Args; #[derive(Debug, Clone, Args)] pub struct PrometheusParams { /// The port used by the prometheus RPC service. - #[arg(long, value_name = "PORT", default_value = "9615")] + #[arg(env = "MADARA_PROMETHEUS_PORT", long, value_name = "PORT", default_value = "9615")] pub prometheus_port: u16, /// Listen on all network interfaces. This usually means the prometheus server will be accessible externally. - #[arg(long)] + #[arg(env = "MADARA_PROMETHEUS_EXTERNAL", long)] pub prometheus_external: bool, /// Disable the prometheus service. - #[arg(long, alias = "no-prometheus")] + #[arg(env = "MADARA_PROMETHEUS_DISABLED", long, alias = "no-prometheus")] pub prometheus_disabled: bool, } diff --git a/crates/node/src/cli/rpc.rs b/crates/node/src/cli/rpc.rs index a7f4240b8..48d18d8a5 100644 --- a/crates/node/src/cli/rpc.rs +++ b/crates/node/src/cli/rpc.rs @@ -69,17 +69,18 @@ impl FromStr for Cors { #[derive(Clone, Debug, clap::Args)] pub struct RpcParams { /// Disable the RPC server. - #[arg(long, alias = "no-rpc")] + #[arg(env = "MADARA_RPC_DISABLED", long, alias = "no-rpc")] pub rpc_disabled: bool, /// Listen to all network interfaces. This usually means that the RPC server will be accessible externally. /// Please note that some endpoints should not be exposed to the outside world - by default, enabling remote access /// will disable these endpoints. To re-enable them, use `--rpc-methods unsafe` - #[arg(long)] + #[arg(env = "MADARA_RPC_EXTERNAL", long)] pub rpc_external: bool, /// RPC methods to expose. #[arg( + env = "MADARA_RPC_METHODS", long, value_name = "METHOD", value_enum, @@ -95,13 +96,13 @@ pub struct RpcParams { /// /// For example `--rpc-rate-limit 10` will maximum allow /// 10 calls per minute per connection. - #[arg(long)] + #[arg(env = "MADARA_RPC_RATE_LIMIT", long)] pub rpc_rate_limit: Option, /// Disable RPC rate limiting for certain ip addresses or ranges. /// /// Each IP address must be in the following notation: `1.2.3.4/24`. - #[arg(long, num_args = 1..)] + #[arg(env = "MADARA_RPC_RATE_LIMIT_WHITELISTED_IPS", long, num_args = 1..)] pub rpc_rate_limit_whitelisted_ips: Vec, /// Trust proxy headers for disable rate limiting. @@ -110,40 +111,40 @@ pub struct RpcParams { /// By default, the RPC server will not trust these headers. /// /// This is currently only useful for rate-limiting reasons. - #[arg(long)] + #[arg(env = "MADARA_RPC_RATE_LIMIT_TRUST_PROXY_HEADERS", long)] pub rpc_rate_limit_trust_proxy_headers: bool, /// Set the maximum RPC request payload size for both HTTP and WebSockets in megabytes. - #[arg(long, default_value_t = RPC_DEFAULT_MAX_REQUEST_SIZE_MB)] + #[arg(env = "MADARA_RPC_MAX_REQUEST_SIZE", long, default_value_t = RPC_DEFAULT_MAX_REQUEST_SIZE_MB)] pub rpc_max_request_size: u32, /// Set the maximum RPC response payload size for both HTTP and WebSockets in megabytes. - #[arg(long, default_value_t = RPC_DEFAULT_MAX_RESPONSE_SIZE_MB)] + #[arg(env = "MADARA_RPC_MAX_RESPONSE_SIZE", long, default_value_t = RPC_DEFAULT_MAX_RESPONSE_SIZE_MB)] pub rpc_max_response_size: u32, /// Set the maximum concurrent subscriptions per connection. - #[arg(long, default_value_t = RPC_DEFAULT_MAX_SUBS_PER_CONN)] + #[arg(env = "MADARA_RPC_MAX_SUBSCRIPTIONS_PER_CONNECTION", long, default_value_t = RPC_DEFAULT_MAX_SUBS_PER_CONN)] pub rpc_max_subscriptions_per_connection: u32, /// The RPC port to listen at. - #[arg(long, value_name = "PORT", default_value_t = RPC_DEFAULT_PORT)] + #[arg(env = "MADARA_RPC_PORT", long, value_name = "PORT", default_value_t = RPC_DEFAULT_PORT)] pub rpc_port: u16, /// Maximum number of RPC server connections at a given time. - #[arg(long, value_name = "COUNT", default_value_t = RPC_DEFAULT_MAX_CONNECTIONS)] + #[arg(env = "MADARA_RPC_MAX_CONNECTIONS", long, value_name = "COUNT", default_value_t = RPC_DEFAULT_MAX_CONNECTIONS)] pub rpc_max_connections: u32, /// The maximum number of messages that can be kept in memory at a given time, per connection. /// The server enforces backpressure, and this buffering is useful when the client cannot keep up with our server. - #[arg(long, default_value_t = RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN)] + #[arg(env = "MADARA_RPC_MESSAGE_BUFFER_CAPACITY_PER_CONNECTION", long, default_value_t = RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN)] pub rpc_message_buffer_capacity_per_connection: u32, /// Disable RPC batch requests. - #[arg(long, alias = "rpc_no_batch_requests", conflicts_with_all = &["rpc_max_batch_request_len"])] + #[arg(env = "MADARA_RPC_DISABLE_BATCH_REQUESTS", long, alias = "rpc_no_batch_requests", conflicts_with_all = &["rpc_max_batch_request_len"])] pub rpc_disable_batch_requests: bool, /// Limit the max length for an RPC batch request. - #[arg(long, conflicts_with_all = &["rpc_disable_batch_requests"], value_name = "LEN")] + #[arg(env = "MADARA_RPC_MAX_BATCH_REQUEST_LEN", long, conflicts_with_all = &["rpc_disable_batch_requests"], value_name = "LEN")] pub rpc_max_batch_request_len: Option, /// Specify browser *origins* allowed to access the HTTP & WebSocket RPC servers. @@ -154,7 +155,7 @@ pub struct RpcParams { /// This argument is a comma separated list of origins, or the special `all` value. /// /// Learn more about CORS and web security at . - #[arg(long, value_name = "ORIGINS")] + #[arg(env = "MADARA_RPC_CORS", long, value_name = "ORIGINS")] pub rpc_cors: Option, } diff --git a/crates/node/src/cli/sync.rs b/crates/node/src/cli/sync.rs index 122f6ee9d..6d72efd56 100644 --- a/crates/node/src/cli/sync.rs +++ b/crates/node/src/cli/sync.rs @@ -11,29 +11,30 @@ use crate::cli::NetworkType; #[derive(Clone, Debug, clap::Args)] pub struct SyncParams { /// Disable the sync service. The sync service is responsible for listening for new blocks on starknet and ethereum. - #[clap(long, alias = "no-sync")] + #[clap(env = "MADARA_SYNC_DISABLED", long, alias = "no-sync")] pub sync_disabled: bool, /// The block you want to start syncing from. This will most probably break your database. - #[clap(long, value_name = "BLOCK NUMBER")] + #[clap(env = "MADARA_UNSAFE_STARTING_BLOCK", long, value_name = "BLOCK NUMBER")] pub unsafe_starting_block: Option, /// Disable state root verification. When importing a block, the state root verification is the most expensive operation. /// Disabling it will mean the sync service will have a huge speed-up, at a security cost // TODO(docs): explain the security cost - #[clap(long)] + #[clap(env = "MADARA_DISABLE_ROOT", long)] pub disable_root: bool, /// Gateway api key to avoid rate limiting (optional). - #[clap(long, value_name = "API KEY")] + #[clap(env = "MADARA_GATEWAY_KEY", long, value_name = "API KEY")] pub gateway_key: Option, /// Feeder gateway url used to sync blocks, state updates and classes - #[clap(long, value_parser = parse_url, value_name = "URL")] + #[clap(env = "MADARA_GATEWAY_URL", long, value_parser = parse_url, value_name = "URL")] pub gateway_url: Option, /// Polling interval, in seconds. This only affects the sync service once it has caught up with the blockchain tip. #[clap( + env = "MADARA_SYNC_POLLING_INTERVAL", long, value_parser = parse_duration, default_value = "4s", @@ -44,6 +45,7 @@ pub struct SyncParams { /// Pending block polling interval, in seconds. This only affects the sync service once it has caught up with the blockchain tip. #[clap( + env = "MADARA_PENDING_BLOCK_POLL_INTERVAL", long, value_parser = parse_duration, default_value = "2s", @@ -54,15 +56,15 @@ pub struct SyncParams { /// Disable sync polling. This currently means that the sync process will not import any more block once it has caught up with the /// blockchain tip. - #[clap(long)] + #[clap(env = "MADARA_NO_SYNC_POLLING", long)] pub no_sync_polling: bool, /// Number of blocks to sync. May be useful for benchmarking the sync service. - #[clap(long, value_name = "NUMBER OF BLOCKS")] + #[clap(env = "MADARA_N_BLOCKS_TO_SYNC", long, value_name = "NUMBER OF BLOCKS")] pub n_blocks_to_sync: Option, /// Periodically create a backup, for debugging purposes. Use it with `--backup-dir `. - #[clap(long, value_name = "NUMBER OF BLOCKS")] + #[clap(env = "MADARA_BACKUP_EVERY_N_BLOCKS", long, value_name = "NUMBER OF BLOCKS")] pub backup_every_n_blocks: Option, } diff --git a/crates/node/src/cli/telemetry.rs b/crates/node/src/cli/telemetry.rs index 9cfca9bec..aa794e21f 100644 --- a/crates/node/src/cli/telemetry.rs +++ b/crates/node/src/cli/telemetry.rs @@ -5,7 +5,7 @@ use clap::Args; pub struct TelemetryParams { /// Disable connecting to the Madara telemetry server. /// Telemetry is enabled by default. - #[arg(long, alias = "no-telemetry")] + #[arg(env = "MADARA_TELEMETRY_DISABLED", long, alias = "no-telemetry")] pub telemetry_disabled: bool, /// The URL of the telemetry server. @@ -14,6 +14,7 @@ pub struct TelemetryParams { /// the least verbosity. /// Expected format is 'URL VERBOSITY', e.g. `--telemetry-url 'wss://foo/bar 0'`. #[arg( + env = "MADARA_TELEMETRY_URL", long = "telemetry-url", value_name = "URL VERBOSITY", value_parser = parse_telemetry_endpoints, diff --git a/crates/primitives/chain_config/Cargo.toml b/crates/primitives/chain_config/Cargo.toml index 9043fa669..46008f821 100644 --- a/crates/primitives/chain_config/Cargo.toml +++ b/crates/primitives/chain_config/Cargo.toml @@ -21,11 +21,11 @@ mp-utils.workspace = true # Other anyhow = { workspace = true } lazy_static = { workspace = true } -log = { workspace = true } primitive-types.workspace = true -reqwest = { workspace = true } -rstest = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } serde_yaml = { workspace = true } thiserror = { workspace = true } + +[dev-dependencies] +rstest = { workspace = true } diff --git a/crates/primitives/chain_config/src/chain_config.rs b/crates/primitives/chain_config/src/chain_config.rs index 1e098052c..5a2435474 100644 --- a/crates/primitives/chain_config/src/chain_config.rs +++ b/crates/primitives/chain_config/src/chain_config.rs @@ -1,3 +1,4 @@ +use std::fmt; // Note: We are NOT using fs read for constants, as they NEED to be included in the resulting // binary. Otherwise, using the madara binary without cloning the repo WILL crash, and that's very very bad. // The binary needs to be self contained! We need to be able to ship madara as a single binary, without @@ -17,7 +18,8 @@ use blockifier::bouncer::{BouncerWeights, BuiltinCount}; use blockifier::{bouncer::BouncerConfig, versioned_constants::VersionedConstants}; use lazy_static::__Deref; use primitive_types::H160; -use serde::{Deserialize, Deserializer}; +use serde::de::{MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; use starknet_api::core::{ChainId, ContractAddress, PatriciaKey}; use starknet_types_core::felt::Felt; @@ -64,6 +66,7 @@ pub struct ChainConfig { pub parent_fee_token_address: ContractAddress, /// BTreeMap ensures order. + #[serde(default)] pub versioned_constants: ChainVersionedConstants, #[serde(deserialize_with = "deserialize_starknet_version")] @@ -104,7 +107,24 @@ pub struct ChainConfig { impl ChainConfig { pub fn from_yaml(path: &Path) -> anyhow::Result { let config_str = fs::read_to_string(path)?; - serde_yaml::from_str(&config_str).context("While deserializing chain config") + let config_value: serde_yaml::Value = + serde_yaml::from_str(&config_str).context("While deserializing chain config")?; + + let versioned_constants_file_paths: BTreeMap = + serde_yaml::from_value(config_value.get("versioned_constants_path").cloned().unwrap_or_default()) + .context("While deserializing versioned constants file paths")?; + + let versioned_constants = { + // add the defaults VersionedConstants + let mut versioned_constants = ChainVersionedConstants::default(); + versioned_constants.merge(ChainVersionedConstants::from_file(versioned_constants_file_paths)?); + versioned_constants + }; + + let chain_config: ChainConfig = + serde_yaml::from_str(&config_str).context("While deserializing chain config")?; + + Ok(ChainConfig { versioned_constants, ..chain_config }) } /// Verify that the chain config is valid for block production. @@ -144,13 +164,7 @@ impl ChainConfig { )) .unwrap(), ), - versioned_constants: [ - (StarknetVersion::V0_13_0, BLOCKIFIER_VERSIONED_CONSTANTS_0_13_0.deref().clone()), - (StarknetVersion::V0_13_1, BLOCKIFIER_VERSIONED_CONSTANTS_0_13_1.deref().clone()), - (StarknetVersion::V0_13_1_1, BLOCKIFIER_VERSIONED_CONSTANTS_0_13_1_1.deref().clone()), - (StarknetVersion::V0_13_2, VersionedConstants::latest_constants().clone()), - ] - .into(), + versioned_constants: ChainVersionedConstants::default(), eth_core_contract_address: eth_core_contract_address::MAINNET.parse().expect("parsing a constant"), @@ -250,44 +264,81 @@ impl ChainConfig { // TODO: the motivation for these doc comments is to move them into a proper app chain developer documentation, with a // proper page about tuning the block production performance. -#[derive(Debug, Default)] +#[derive(Debug)] pub struct ChainVersionedConstants(pub BTreeMap); -impl From<[(StarknetVersion, VersionedConstants); N]> for ChainVersionedConstants { - fn from(arr: [(StarknetVersion, VersionedConstants); N]) -> Self { - ChainVersionedConstants(arr.into_iter().collect()) - } -} - -/// Replaces the versioned_constants files definition in the yaml by the content of the -/// jsons. impl<'de> Deserialize<'de> for ChainVersionedConstants { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - let file_paths: BTreeMap = Deserialize::deserialize(deserializer)?; + struct ChainVersionedConstantsVisitor; + + impl<'de> Visitor<'de> for ChainVersionedConstantsVisitor { + type Value = ChainVersionedConstants; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a map of StarknetVersion to VersionedConstants") + } + + fn visit_map(self, mut access: M) -> Result + where + M: MapAccess<'de>, + { + let mut map = BTreeMap::new(); + while let Some((key, value)) = access.next_entry::()? { + map.insert(key.parse().map_err(serde::de::Error::custom)?, value); + } + Ok(ChainVersionedConstants(map)) + } + } + + deserializer.deserialize_map(ChainVersionedConstantsVisitor) + } +} + +impl From<[(StarknetVersion, VersionedConstants); N]> for ChainVersionedConstants { + fn from(arr: [(StarknetVersion, VersionedConstants); N]) -> Self { + ChainVersionedConstants(arr.into()) + } +} + +impl Default for ChainVersionedConstants { + fn default() -> Self { + [ + (StarknetVersion::V0_13_0, BLOCKIFIER_VERSIONED_CONSTANTS_0_13_0.deref().clone()), + (StarknetVersion::V0_13_1, BLOCKIFIER_VERSIONED_CONSTANTS_0_13_1.deref().clone()), + (StarknetVersion::V0_13_1_1, BLOCKIFIER_VERSIONED_CONSTANTS_0_13_1_1.deref().clone()), + (StarknetVersion::V0_13_2, VersionedConstants::latest_constants().clone()), + ] + .into() + } +} + +impl ChainVersionedConstants { + pub fn add(&mut self, version: StarknetVersion, constants: VersionedConstants) { + self.0.insert(version, constants); + } + + pub fn merge(&mut self, other: Self) { + self.0.extend(other.0); + } + + pub fn from_file(version_with_path: BTreeMap) -> Result { let mut result = BTreeMap::new(); - for (version, path) in file_paths { + for (version, path) in version_with_path { // Change the current directory to Madara root - let mut file = File::open(Path::new(&path)) - .with_context(|| format!("Failed to open file: {}", path)) - .map_err(serde::de::Error::custom)?; + let mut file = File::open(Path::new(&path)).with_context(|| format!("Failed to open file: {}", path))?; let mut contents = String::new(); - file.read_to_string(&mut contents) - .with_context(|| format!("Failed to read contents of file: {}", path)) - .map_err(serde::de::Error::custom)?; + file.read_to_string(&mut contents).with_context(|| format!("Failed to read contents of file: {}", path))?; - let constants: VersionedConstants = serde_json::from_str(&contents) - .with_context(|| format!("Failed to parse JSON in file: {}", path)) - .map_err(serde::de::Error::custom)?; + let constants: VersionedConstants = + serde_json::from_str(&contents).with_context(|| format!("Failed to parse JSON in file: {}", path))?; - let parsed_version = version - .parse() - .with_context(|| format!("Failed to parse version string: {}", version)) - .map_err(serde::de::Error::custom)?; + let parsed_version = + version.parse().with_context(|| format!("Failed to parse version string: {}", version))?; result.insert(parsed_version, constants); } @@ -304,6 +355,13 @@ where StarknetVersion::from_str(&s).map_err(serde::de::Error::custom) } +pub fn serialize_starknet_version(version: &StarknetVersion, serializer: S) -> Result +where + S: serde::Serializer, +{ + version.to_string().serialize(serializer) +} + // TODO: this is workaround because BouncerConfig doesn't derive Deserialize in blockifier pub fn deserialize_bouncer_config<'de, D>(deserializer: D) -> Result where @@ -318,6 +376,18 @@ where Ok(BouncerConfig { block_max_capacity: helper.block_max_capacity }) } +pub fn serialize_bouncer_config(config: &BouncerConfig, serializer: S) -> Result +where + S: serde::Serializer, +{ + #[derive(Serialize)] + struct BouncerConfigHelper<'a> { + block_max_capacity: &'a BouncerWeights, + } + + BouncerConfigHelper { block_max_capacity: &config.block_max_capacity }.serialize(serializer) +} + #[cfg(test)] mod tests { use blockifier::{transaction::transaction_types::TransactionType, versioned_constants::ResourceCost}; @@ -409,7 +479,13 @@ mod tests { assert_eq!(chain_config.bouncer_config.block_max_capacity.state_diff_size, 131072); assert_eq!(chain_config.bouncer_config.block_max_capacity.builtin_count.add_mod, 18446744073709551615); - assert_eq!(chain_config.sequencer_address, ContractAddress::try_from(Felt::from_str("0x0").unwrap()).unwrap()); + assert_eq!( + chain_config.sequencer_address, + ContractAddress::try_from( + Felt::from_str("0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8").unwrap() + ) + .unwrap() + ); assert_eq!(chain_config.max_nonce_for_validation_skip, 2); assert_eq!( chain_config.eth_core_contract_address, diff --git a/crates/primitives/class/Cargo.toml b/crates/primitives/class/Cargo.toml index 103324bb6..f9d33b05d 100644 --- a/crates/primitives/class/Cargo.toml +++ b/crates/primitives/class/Cargo.toml @@ -31,13 +31,11 @@ starknet-core = { workspace = true } starknet-types-core = { workspace = true } # Other -anyhow = { workspace = true } flate2 = { workspace = true } lazy_static = { workspace = true } num-bigint = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } -sha3 = { workspace = true } thiserror = { workspace = true } [dev-dependencies] diff --git a/crates/primitives/receipt/Cargo.toml b/crates/primitives/receipt/Cargo.toml index 6c30ecedf..b25d02475 100644 --- a/crates/primitives/receipt/Cargo.toml +++ b/crates/primitives/receipt/Cargo.toml @@ -25,7 +25,6 @@ starknet-providers = { workspace = true } starknet-types-core = { workspace = true } starknet_api = { workspace = true } # Other -log = { workspace = true } serde = { workspace = true, features = ["derive"] } [dev-dependencies] diff --git a/crates/primitives/utils/Cargo.toml b/crates/primitives/utils/Cargo.toml index 672af82d5..799c81cfe 100644 --- a/crates/primitives/utils/Cargo.toml +++ b/crates/primitives/utils/Cargo.toml @@ -20,5 +20,6 @@ futures.workspace = true rayon.workspace = true rstest = { workspace = true } serde.workspace = true +serde_yaml.workspace = true tokio = { workspace = true, features = ["signal"] } url.workspace = true diff --git a/crates/primitives/utils/src/parsers.rs b/crates/primitives/utils/src/parsers.rs index 2047630ec..6619c9993 100644 --- a/crates/primitives/utils/src/parsers.rs +++ b/crates/primitives/utils/src/parsers.rs @@ -1,15 +1,22 @@ -use anyhow::{anyhow, bail}; +use anyhow::{anyhow, bail, ensure}; +use serde_yaml::Value; use std::time::Duration; use url::Url; -/// Parses a "key=value" string & returns a [(String, String)] tuple. -pub fn parse_key_value(s: &str) -> anyhow::Result<(String, String)> { +/// Parses a "key=value" string & returns a [(String, Value)] tuple. +pub fn parse_key_value_yaml(s: &str) -> anyhow::Result<(String, Value)> { let mut parts = s.splitn(2, '='); - let key = parts.next().ok_or_else(|| anyhow::anyhow!("Invalid key-value pair"))?; - let value = parts.next().ok_or_else(|| anyhow::anyhow!("Invalid key-value pair"))?; - Ok((key.to_string(), value.to_string())) + let key = parts.next().ok_or_else(|| anyhow!("Missing key in key-value pair"))?.trim(); + let value = parts.next().ok_or_else(|| anyhow!("Missing value in key-value pair"))?.trim(); + + ensure!(!key.trim().is_empty(), "Key cannot be empty"); + + // If the value starts with "0x", treat it as a string (to avoid parsing Felt values as numbers) + let value = if value.starts_with("0x") { Value::String(value.to_string()) } else { serde_yaml::from_str(value)? }; + + Ok((key.to_string(), value)) } /// Parse a string URL & returns it as [Url]. diff --git a/crates/primitives/utils/src/serde.rs b/crates/primitives/utils/src/serde.rs index 50b73753b..a3be87c18 100644 --- a/crates/primitives/utils/src/serde.rs +++ b/crates/primitives/utils/src/serde.rs @@ -11,3 +11,14 @@ where let s = String::deserialize(deserializer)?; parse_duration(&s).map_err(serde::de::Error::custom) } + +pub fn serialize_duration(duration: &Duration, serializer: S) -> Result +where + S: serde::Serializer, +{ + if duration.as_secs_f64().fract() == 0.0 { + serializer.serialize_str(&format!("{}s", duration.as_secs())) + } else { + serializer.serialize_str(&format!("{}ms", duration.as_millis())) + } +} diff --git a/crates/tests/Cargo.toml b/crates/tests/Cargo.toml index 899a11307..af860fa7f 100644 --- a/crates/tests/Cargo.toml +++ b/crates/tests/Cargo.toml @@ -8,16 +8,10 @@ version.workspace = true license.workspace = true [dependencies] - -# Madara - -mp-utils.workspace = true - anyhow.workspace = true env_logger.workspace = true flate2 = "1.0.30" lazy_static.workspace = true -once_cell = { workspace = true } reqwest.workspace = true rstest.workspace = true serde_json = { workspace = true }