diff --git a/CHANGELOG.md b/CHANGELOG.md index 009eac1bf..d0a55781b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Next release +- refactor: calculate class hashes in devnet - feat: add config file and preset configure chain - refactor: change default chain id and add custom flag to override - fix: generate a fixed set of public and private keys for devnet diff --git a/crates/client/db/src/devnet_db.rs b/crates/client/db/src/devnet_db.rs index 7f8e11730..084a73d2d 100644 --- a/crates/client/db/src/devnet_db.rs +++ b/crates/client/db/src/devnet_db.rs @@ -9,14 +9,15 @@ pub const DEVNET_KEYS: &[u8] = b"DEVNET_KEYS"; type Result = std::result::Result; #[derive(Clone, Serialize, Deserialize)] -pub struct DevnetPredeployedContractKey { +pub struct DevnetPredeployedContractAccount { pub address: Felt, pub secret: Felt, pub pubkey: Felt, + pub class_hash: Felt, } #[derive(Clone, Serialize, Deserialize)] -pub struct DevnetPredeployedKeys(pub Vec); +pub struct DevnetPredeployedKeys(pub Vec); impl MadaraBackend { /// Get the devnet predeployed contracts keys. diff --git a/crates/client/devnet/src/classes.rs b/crates/client/devnet/src/classes.rs index e93d240d9..e1b27b415 100644 --- a/crates/client/devnet/src/classes.rs +++ b/crates/client/devnet/src/classes.rs @@ -1,20 +1,22 @@ use anyhow::Context; use mc_block_import::{DeclaredClass, LegacyDeclaredClass, SierraDeclaredClass}; +use mp_class::{CompressedLegacyContractClass, FlattenedSierraClass}; use mp_state_update::DeclaredClassItem; +use starknet_core::types::contract::{legacy::LegacyContractClass, SierraClass}; use starknet_types_core::felt::Felt; use std::collections::HashMap; #[derive(Clone, Debug)] pub struct InitiallyDeclaredSierraClass { + pub contract_class: FlattenedSierraClass, pub class_hash: Felt, pub compiled_class_hash: Felt, - pub definition: Vec, } #[derive(Clone, Debug)] pub struct InitiallyDeclaredLegacyClass { + pub contract_class: CompressedLegacyContractClass, pub class_hash: Felt, - pub definition: Vec, } #[derive(Clone, Debug)] @@ -24,11 +26,23 @@ pub enum InitiallyDeclaredClass { } impl InitiallyDeclaredClass { - pub fn new_sierra(class_hash: Felt, compiled_class_hash: Felt, definition: impl Into>) -> Self { - Self::Sierra(InitiallyDeclaredSierraClass { class_hash, compiled_class_hash, definition: definition.into() }) + pub fn new_sierra(definition: impl Into>) -> anyhow::Result { + let class = serde_json::from_slice::(&definition.into()) + .with_context(|| "Deserializing sierra class".to_string())?; + let contract_class: FlattenedSierraClass = + class.flatten().with_context(|| "Flattening sierra class".to_string())?.into(); + let class_hash = contract_class.compute_class_hash().context("Computing sierra class hash")?; + let (compiled_class_hash, _) = contract_class.compile_to_casm().context("Compiling sierra class")?; + + Ok(Self::Sierra(InitiallyDeclaredSierraClass { contract_class, class_hash, compiled_class_hash })) } - pub fn new_legacy(class_hash: Felt, definition: impl Into>) -> Self { - Self::Legacy(InitiallyDeclaredLegacyClass { class_hash, definition: definition.into() }) + pub fn new_legacy(definition: impl Into>) -> anyhow::Result { + let contract_class = serde_json::from_slice::(&definition.into()) + .with_context(|| "Deserializing legacy class".to_string())?; + let class_hash = contract_class.class_hash().context("Computing legacy class hash")?; + let contract_class = contract_class.compress().context("Compressing legacy")?.into(); + + Ok(Self::Legacy(InitiallyDeclaredLegacyClass { contract_class, class_hash })) } pub fn class_hash(&self) -> Felt { @@ -79,36 +93,19 @@ impl InitiallyDeclaredClasses { } /// Load the classes into `DeclaredClass`es. - pub fn into_loaded_classes(self) -> anyhow::Result> { - use starknet_core::types::contract::{legacy::LegacyContractClass, SierraClass}; - + pub fn into_loaded_classes(self) -> Vec { self.0 - .into_iter() - .enumerate() - .map(|(i, (class_hash, class))| { - let make_err_ctx = - |what: &'static str| move || format!("{what} class with hash {class_hash:#} (index {i})"); - match class { - InitiallyDeclaredClass::Sierra(c) => { - let compiled_class_hash = c.compiled_class_hash; - let class = serde_json::from_slice::(&c.definition) - .with_context(make_err_ctx("Deserializing sierra"))?; - let class = class.flatten().with_context(make_err_ctx("Flattening sierra"))?; - - Ok(DeclaredClass::Sierra(SierraDeclaredClass { - class_hash, - contract_class: class.into(), - compiled_class_hash, - })) - } - InitiallyDeclaredClass::Legacy(c) => { - let class = serde_json::from_slice::(&c.definition) - .with_context(make_err_ctx("Deserializing legacy"))?; - let class = class.compress().context("Compressing legacy")?; - - Ok(DeclaredClass::Legacy(LegacyDeclaredClass { class_hash, contract_class: class.into() })) - } - } + .into_values() + .map(|class| match class { + InitiallyDeclaredClass::Sierra(c) => DeclaredClass::Sierra(SierraDeclaredClass { + class_hash: c.class_hash, + contract_class: c.contract_class, + compiled_class_hash: c.compiled_class_hash, + }), + InitiallyDeclaredClass::Legacy(c) => DeclaredClass::Legacy(LegacyDeclaredClass { + class_hash: c.class_hash, + contract_class: c.contract_class, + }), }) .collect() } diff --git a/crates/client/devnet/src/lib.rs b/crates/client/devnet/src/lib.rs index a6fd54e4b..5a629c71e 100644 --- a/crates/client/devnet/src/lib.rs +++ b/crates/client/devnet/src/lib.rs @@ -1,3 +1,4 @@ +use anyhow::Context; use blockifier::abi::abi_utils::get_storage_var_address; use mc_block_import::{UnverifiedFullBlock, UnverifiedHeader}; use mp_block::header::GasPrices; @@ -52,19 +53,11 @@ impl StorageDiffs { /// Universal Deployer Contract. const UDC_CLASS_DEFINITION: &[u8] = include_bytes!("../../../../cairo/target/dev/madara_contracts_UniversalDeployer.contract_class.json"); -const UDC_CLASS_HASH: Felt = - Felt::from_hex_unchecked("0x01e947be496dfd19a635fdc32d34528c9074acf96427da4700f3fa6c933fdb02"); const UDC_CONTRACT_ADDRESS: Felt = Felt::from_hex_unchecked("0x041a78e741e5af2fec34b695679bc6891742439f7afb8484ecd7766661ad02bf"); -const UDC_COMPILED_CLASS_HASH: Felt = - Felt::from_hex_unchecked("0x44d7658791f01f2936b045c09df6628997437a7321c4f682dddf4ff5380993d"); const ERC20_CLASS_DEFINITION: &[u8] = include_bytes!("../../../../cairo/target/dev/madara_contracts_ERC20.contract_class.json"); -const ERC20_CLASS_HASH: Felt = - Felt::from_hex_unchecked("0x233e7094e9e971bf0a5c0d999e7f2ae4f820dcb1304c00e3589a913423ab204"); -const ERC20_COMPILED_CLASS_HASH: Felt = - Felt::from_hex_unchecked("0x639b7f3c30a7136d13d63c16db7fa15399bd2624d60f2f3ab78d6eae3d6a4e5"); const ERC20_STRK_CONTRACT_ADDRESS: Felt = Felt::from_hex_unchecked("0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d"); const ERC20_ETH_CONTRACT_ADDRESS: Felt = @@ -72,10 +65,6 @@ const ERC20_ETH_CONTRACT_ADDRESS: Felt = const ACCOUNT_CLASS_DEFINITION: &[u8] = include_bytes!("../../../../cairo/target/dev/madara_contracts_AccountUpgradeable.contract_class.json"); -const ACCOUNT_CLASS_HASH: Felt = - Felt::from_hex_unchecked("0x7446579979174f1687e030b2da6a0bf41ec995a206ddf314030e504536c61c1"); -const ACCOUNT_COMPILED_CLASS_HASH: Felt = - Felt::from_hex_unchecked("0x138105ded3d2e4ea1939a0bc106fb80fd8774c9eb89c1890d4aeac88e6a1b27"); /// High level description of the genesis block. #[derive(Clone, Debug, Default)] @@ -88,30 +77,26 @@ pub struct ChainGenesisDescription { } impl ChainGenesisDescription { - pub fn base_config() -> Self { - Self { + pub fn base_config() -> anyhow::Result { + let udc_class = InitiallyDeclaredClass::new_sierra(UDC_CLASS_DEFINITION).context("Failed to add UDC class")?; + let erc20_class = + InitiallyDeclaredClass::new_sierra(ERC20_CLASS_DEFINITION).context("Failed to add ERC20 class")?; + Ok(Self { initial_balances: InitialBalances::default(), - declared_classes: InitiallyDeclaredClasses::default() - .with(InitiallyDeclaredClass::new_sierra(UDC_CLASS_HASH, UDC_COMPILED_CLASS_HASH, UDC_CLASS_DEFINITION)) - .with(InitiallyDeclaredClass::new_sierra( - ERC20_CLASS_HASH, - ERC20_COMPILED_CLASS_HASH, - ERC20_CLASS_DEFINITION, - )), deployed_contracts: InitiallyDeployedContracts::default() - .with(UDC_CONTRACT_ADDRESS, UDC_CLASS_HASH) - .with(ERC20_ETH_CONTRACT_ADDRESS, ERC20_CLASS_HASH) - .with(ERC20_STRK_CONTRACT_ADDRESS, ERC20_CLASS_HASH), + .with(UDC_CONTRACT_ADDRESS, udc_class.class_hash()) + .with(ERC20_ETH_CONTRACT_ADDRESS, erc20_class.class_hash()) + .with(ERC20_STRK_CONTRACT_ADDRESS, erc20_class.class_hash()), + declared_classes: InitiallyDeclaredClasses::default().with(udc_class).with(erc20_class), initial_storage: StorageDiffs::default(), - } + }) } - pub fn add_devnet_contracts(&mut self, n_addr: u64) -> DevnetKeys { - self.declared_classes.insert(InitiallyDeclaredClass::new_sierra( - ACCOUNT_CLASS_HASH, - ACCOUNT_COMPILED_CLASS_HASH, - ACCOUNT_CLASS_DEFINITION, - )); + pub fn add_devnet_contracts(&mut self, n_addr: u64) -> anyhow::Result { + let account_class = + InitiallyDeclaredClass::new_sierra(ACCOUNT_CLASS_DEFINITION).context("Failed to add account class")?; + let account_class_hash = account_class.class_hash(); + self.declared_classes.insert(account_class); fn get_contract_pubkey_storage_address() -> StorageKey { get_storage_var_address("Account_public_key", &[]) @@ -126,7 +111,7 @@ impl ChainGenesisDescription { Felt::from_bytes_be_slice(&buffer) } - DevnetKeys( + Ok(DevnetKeys( (0..n_addr) .map(|addr_idx| { let secret_scalar = from_seed(addr_idx); @@ -135,14 +120,14 @@ impl ChainGenesisDescription { // calculating actual address w.r.t. the class hash. let calculated_address = - calculate_contract_address(Felt::ZERO, ACCOUNT_CLASS_HASH, &[pubkey.scalar()], Felt::ZERO); + calculate_contract_address(Felt::ZERO, account_class_hash, &[pubkey.scalar()], Felt::ZERO); let balance = ContractFeeTokensBalance { fri: (10_000 * ETH_WEI_DECIMALS).into(), wei: (10_000 * STRK_FRI_DECIMALS).into(), }; - self.deployed_contracts.insert(calculated_address, ACCOUNT_CLASS_HASH); + self.deployed_contracts.insert(calculated_address, account_class_hash); self.initial_balances .insert(ContractAddress::try_from(calculated_address).unwrap(), balance.clone()); self.initial_storage @@ -154,10 +139,11 @@ impl ChainGenesisDescription { pubkey: pubkey.scalar(), balance, address: calculated_address, + class_hash: account_class_hash, } }) .collect(), - ) + )) } pub fn build(mut self, chain_config: &ChainConfig) -> anyhow::Result { @@ -188,7 +174,7 @@ impl ChainGenesisDescription { replaced_classes: vec![], nonces: vec![], }, - declared_classes: self.declared_classes.into_loaded_classes()?, + declared_classes: self.declared_classes.into_loaded_classes(), unverified_block_number: Some(0), ..Default::default() }) @@ -309,8 +295,8 @@ mod tests { fn chain() -> DevnetForTesting { let _ = env_logger::builder().is_test(true).try_init(); - let mut g = ChainGenesisDescription::base_config(); - let contracts = g.add_devnet_contracts(10); + let mut g = ChainGenesisDescription::base_config().unwrap(); + let contracts = g.add_devnet_contracts(10).unwrap(); let chain_config = Arc::new(ChainConfig::test_config().unwrap()); let block = g.build(&chain_config).unwrap(); @@ -414,16 +400,16 @@ mod tests { #[rstest] fn test_account_deploy(_set_workdir: (), mut chain: DevnetForTesting) { - println!("{}", chain.contracts); - let key = SigningKey::from_random(); log::debug!("Secret Key : {:?}", key.secret_scalar()); let pubkey = key.verifying_key(); log::debug!("Public Key : {:?}", pubkey.scalar()); + // using the class hash of the first account as the account class hash + let account_class_hash = chain.contracts.0[0].class_hash; let calculated_address = - calculate_contract_address(Felt::ZERO, ACCOUNT_CLASS_HASH, &[pubkey.scalar()], Felt::ZERO); + calculate_contract_address(Felt::ZERO, account_class_hash, &[pubkey.scalar()], Felt::ZERO); log::debug!("Calculated Address : {:?}", calculated_address); // ===================================================================================== @@ -469,6 +455,7 @@ mod tests { pubkey: pubkey.scalar(), balance: account_balance, address: calculated_address, + class_hash: account_class_hash, }; let deploy_account_txn = BroadcastedDeployAccountTransaction::V3(BroadcastedDeployAccountTransactionV3 { @@ -476,7 +463,7 @@ mod tests { nonce: Felt::ZERO, contract_address_salt: Felt::ZERO, constructor_calldata: vec![pubkey.scalar()], - class_hash: ACCOUNT_CLASS_HASH, + class_hash: account_class_hash, resource_bounds: ResourceBoundsMapping { l1_gas: ResourceBounds { max_amount: 60000, max_price_per_unit: 10000 }, l2_gas: ResourceBounds { max_amount: 60000, max_price_per_unit: 10000 }, diff --git a/crates/client/devnet/src/predeployed_contracts.rs b/crates/client/devnet/src/predeployed_contracts.rs index 2ccf6fdf3..eb34c604f 100644 --- a/crates/client/devnet/src/predeployed_contracts.rs +++ b/crates/client/devnet/src/predeployed_contracts.rs @@ -16,6 +16,7 @@ pub struct DevnetPredeployedContract { pub secret: SigningKey, pub pubkey: Felt, pub balance: ContractFeeTokensBalance, + pub class_hash: Felt, } pub struct DevnetKeys(pub Vec); @@ -96,6 +97,7 @@ impl DevnetKeys { secret: SigningKey::from_secret_scalar(k.secret), pubkey: k.pubkey, balance: get_fee_tokens_balance(backend, k.address)?, + class_hash: k.class_hash, }) }) .collect::>()?; @@ -107,10 +109,11 @@ impl DevnetKeys { let keys = mc_db::devnet_db::DevnetPredeployedKeys( self.0 .iter() - .map(|k| mc_db::devnet_db::DevnetPredeployedContractKey { + .map(|k| mc_db::devnet_db::DevnetPredeployedContractAccount { address: k.address, secret: k.secret.secret_scalar(), pubkey: k.pubkey, + class_hash: k.class_hash, }) .collect(), ); diff --git a/crates/node/src/service/block_production.rs b/crates/node/src/service/block_production.rs index cd7a7a692..799f1eba6 100644 --- a/crates/node/src/service/block_production.rs +++ b/crates/node/src/service/block_production.rs @@ -71,8 +71,11 @@ impl Service for BlockProductionService { log::info!("⛏️ Deploying devnet genesis block"); - let mut genesis_config = ChainGenesisDescription::base_config(); - let contracts = genesis_config.add_devnet_contracts(n_devnet_contracts); + let mut genesis_config = + ChainGenesisDescription::base_config().context("Failed to create base genesis config")?; + let contracts = genesis_config + .add_devnet_contracts(n_devnet_contracts) + .context("Failed to add devnet contracts")?; let genesis_block = genesis_config .build(backend.chain_config())