Skip to content

Commit

Permalink
fix: override_chain_config
Browse files Browse the repository at this point in the history
  • Loading branch information
jbcaron committed Oct 7, 2024
1 parent bb75fda commit cfe8f5c
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 88 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Next release

- fix: override chain config
- fix:(tests): Add testing feature to mc-db dev dependency (#294)
- feat: new crate gateway client & server
- test: Starknet-js basic tests added
Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion configs/presets/integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,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
2 changes: 1 addition & 1 deletion configs/presets/mainnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,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
2 changes: 1 addition & 1 deletion configs/presets/sepolia.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,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
1 change: 1 addition & 0 deletions crates/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ rayon.workspace = true
reqwest = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
serde_with.workspace = true
serde_yaml.workspace = true
thiserror.workspace = true
tokio = { workspace = true }
Expand Down
204 changes: 127 additions & 77 deletions crates/node/src/cli/chain_config_overrides.rs
Original file line number Diff line number Diff line change
@@ -1,104 +1,154 @@
use std::time::Duration;
use std::{collections::HashMap, str::FromStr, time::Duration};

use anyhow::Context;
use serde::{Deserialize, Serialize};
use clap::Parser;
use serde::{Deserialize, Deserializer};
use serde_with::serde_as;
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_utils::parsers::parse_duration;
use mp_utils::parsers::parse_key_value_yaml;

/// 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=1.5"
#[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(long = "chain-config-override", value_parser = parse_key_value_yaml, use_value_delimiter = true, value_delimiter = ',')]
pub overrides: Vec<(String, Value)>,
}

/// Part of the Chain Config that we can override.
#[serde_as]
#[derive(Debug, Default, Deserialize)]
pub struct ChainConfigOverridesInner {
pub chain_name: Option<String>,
pub chain_id: Option<ChainId>,
pub native_fee_token_address: Option<ContractAddress>,
pub parent_fee_token_address: Option<ContractAddress>,
#[serde(default, deserialize_with = "deserialize_version")]
pub latest_protocol_version: Option<StarknetVersion>,
#[serde(default, deserialize_with = "deserialize_duration")]
pub block_time: Option<Duration>,
#[serde(default, deserialize_with = "deserialize_duration")]
pub pending_block_update_time: Option<Duration>,
pub execution_batch_size: Option<usize>,
pub sequencer_address: Option<ContractAddress>,
pub max_nonce_for_validation_skip: Option<u64>,
pub eth_core_contract_address: Option<H160>,

// bouncer config
pub gas: Option<usize>,
pub message_segment_length: Option<usize>,
pub n_events: Option<usize>,
pub n_steps: Option<usize>,
pub state_diff_size: Option<usize>,
pub add_mod: Option<usize>,
pub bitwise: Option<usize>,
pub ecdsa: Option<usize>,
pub ec_op: Option<usize>,
pub keccak: Option<usize>,
pub mul_mod: Option<usize>,
pub pedersen: Option<usize>,
pub poseidon: Option<usize>,
pub range_check: Option<usize>,
pub range_check96: Option<usize>,
}

impl ChainConfigOverrideParams {
pub fn override_chain_config(&self, chain_config: ChainConfig) -> anyhow::Result<ChainConfig> {
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));
pub fn override_chain_config(&self, chain_config: &mut ChainConfig) -> anyhow::Result<()> {
let overrides_map: HashMap<_, _> = self.overrides.iter().cloned().collect();
let overrides_value = serde_yaml::to_value(overrides_map).context("Failed to convert overrides to Value")?;
let overrides: ChainConfigOverridesInner =
serde_yaml::from_value(overrides_value).context("Failed to parse YAML Value to ChainConfigOverrides")?;

// Update config fields with any provided override values
macro_rules! apply_override {
($config:expr, $field:ident, $overrides:expr) => {
if let Some(value) = &$overrides.$field {
$config.$field = value.clone();
}
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")?;
apply_override!(chain_config, chain_name, overrides);
apply_override!(chain_config, chain_id, overrides);
apply_override!(chain_config, native_fee_token_address, overrides);
apply_override!(chain_config, parent_fee_token_address, overrides);
apply_override!(chain_config, latest_protocol_version, overrides);
apply_override!(chain_config, block_time, overrides);
apply_override!(chain_config, pending_block_update_time, overrides);
apply_override!(chain_config, execution_batch_size, overrides);
apply_override!(chain_config, sequencer_address, overrides);
apply_override!(chain_config, max_nonce_for_validation_skip, overrides);
apply_override!(chain_config, eth_core_contract_address, overrides);

Ok(ChainConfig {
versioned_constants: chain_config.versioned_constants,
bouncer_config: chain_config.bouncer_config,
..updated_overridable.into()
})
}
}
// Apply bouncer configurations
macro_rules! apply_bouncer {
($config:expr, $field:ident, $overrides:expr) => {
if let Some(value) = &$overrides.$field {
$config.bouncer_config.block_max_capacity.$field = *value;
}
};
}

/// 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 chain_name: String,
pub chain_id: ChainId,
pub native_fee_token_address: ContractAddress,
pub parent_fee_token_address: ContractAddress,
pub latest_protocol_version: StarknetVersion,
pub block_time: Duration,
pub pending_block_update_time: Duration,
pub execution_batch_size: usize,
pub sequencer_address: ContractAddress,
pub max_nonce_for_validation_skip: u64,
pub eth_core_contract_address: H160,
apply_bouncer!(chain_config, gas, overrides);
apply_bouncer!(chain_config, message_segment_length, overrides);
apply_bouncer!(chain_config, n_events, overrides);
apply_bouncer!(chain_config, n_steps, overrides);
apply_bouncer!(chain_config, state_diff_size, overrides);

// Apply builtin configurations
macro_rules! apply_builtin {
($config:expr, $field:ident, $overrides:expr) => {
if let Some(value) = &$overrides.$field {
$config.bouncer_config.block_max_capacity.builtin_count.$field = *value;
}
};
}

apply_builtin!(chain_config, add_mod, overrides);
apply_builtin!(chain_config, bitwise, overrides);
apply_builtin!(chain_config, ecdsa, overrides);
apply_builtin!(chain_config, ec_op, overrides);
apply_builtin!(chain_config, keccak, overrides);
apply_builtin!(chain_config, mul_mod, overrides);
apply_builtin!(chain_config, pedersen, overrides);
apply_builtin!(chain_config, poseidon, overrides);
apply_builtin!(chain_config, range_check, overrides);
apply_builtin!(chain_config, range_check96, overrides);

Ok(())
}
}

impl From<&ChainConfig> for OverridableChainConfig {
fn from(config: &ChainConfig) -> Self {
OverridableChainConfig {
chain_name: config.chain_name.clone(),
chain_id: config.chain_id.clone(),
native_fee_token_address: config.native_fee_token_address,
parent_fee_token_address: config.parent_fee_token_address,
latest_protocol_version: config.latest_protocol_version,
block_time: config.block_time,
pending_block_update_time: config.pending_block_update_time,
execution_batch_size: config.execution_batch_size,
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,
pub fn deserialize_version<'de, D>(deserializer: D) -> Result<Option<StarknetVersion>, D::Error>
where
D: Deserializer<'de>,
{
let value: Option<Value> = Option::deserialize(deserializer)?;
match value {
Some(v) => {
let s = v.as_str().ok_or_else(|| serde::de::Error::custom("Expected a string"))?;
StarknetVersion::from_str(s).map_err(serde::de::Error::custom).map(Some)
}
None => Ok(None),
}
}

impl From<OverridableChainConfig> 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(),
pub fn deserialize_duration<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
where
D: Deserializer<'de>,
{
let value: Option<Value> = Option::deserialize(deserializer)?;

match value {
Some(v) => {
let s = v.as_str().ok_or_else(|| serde::de::Error::custom("Expected a string"))?;
parse_duration(s).map_err(serde::de::Error::custom).map(Some)
}
None => Ok(None),
}
}
4 changes: 2 additions & 2 deletions crates/node/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ impl RunCmd {
};

if !self.chain_config_override.overrides.is_empty() {
chain_config = self.chain_config_override.override_chain_config(chain_config)?;
self.chain_config_override.override_chain_config(&mut chain_config)?;
};

Ok(Arc::new(chain_config))
Expand All @@ -170,7 +170,7 @@ impl RunCmd {
};

if !self.chain_config_override.overrides.is_empty() {
chain_config = self.chain_config_override.override_chain_config(chain_config)?;
self.chain_config_override.override_chain_config(&mut chain_config)?;
}

Ok(Arc::new(chain_config))
Expand Down
1 change: 1 addition & 0 deletions crates/primitives/utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
19 changes: 13 additions & 6 deletions crates/primitives/utils/src/parsers.rs
Original file line number Diff line number Diff line change
@@ -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].
Expand Down

0 comments on commit cfe8f5c

Please sign in to comment.