Skip to content

Commit

Permalink
refactor: calculate class hash in devnet (#269)
Browse files Browse the repository at this point in the history
  • Loading branch information
apoorvsadana authored Sep 18, 2024
1 parent 8be242a commit c232ebe
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 82 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

- 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
Expand Down
5 changes: 3 additions & 2 deletions crates/client/db/src/devnet_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ pub const DEVNET_KEYS: &[u8] = b"DEVNET_KEYS";
type Result<T, E = MadaraStorageError> = std::result::Result<T, E>;

#[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<DevnetPredeployedContractKey>);
pub struct DevnetPredeployedKeys(pub Vec<DevnetPredeployedContractAccount>);

impl MadaraBackend {
/// Get the devnet predeployed contracts keys.
Expand Down
67 changes: 32 additions & 35 deletions crates/client/devnet/src/classes.rs
Original file line number Diff line number Diff line change
@@ -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<u8>,
}

#[derive(Clone, Debug)]
pub struct InitiallyDeclaredLegacyClass {
pub contract_class: CompressedLegacyContractClass,
pub class_hash: Felt,
pub definition: Vec<u8>,
}

#[derive(Clone, Debug)]
Expand All @@ -24,11 +26,23 @@ pub enum InitiallyDeclaredClass {
}

impl InitiallyDeclaredClass {
pub fn new_sierra(class_hash: Felt, compiled_class_hash: Felt, definition: impl Into<Vec<u8>>) -> Self {
Self::Sierra(InitiallyDeclaredSierraClass { class_hash, compiled_class_hash, definition: definition.into() })
pub fn new_sierra(definition: impl Into<Vec<u8>>) -> anyhow::Result<Self> {
let class = serde_json::from_slice::<SierraClass>(&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<Vec<u8>>) -> Self {
Self::Legacy(InitiallyDeclaredLegacyClass { class_hash, definition: definition.into() })
pub fn new_legacy(definition: impl Into<Vec<u8>>) -> anyhow::Result<Self> {
let contract_class = serde_json::from_slice::<LegacyContractClass>(&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 {
Expand Down Expand Up @@ -79,36 +93,19 @@ impl InitiallyDeclaredClasses {
}

/// Load the classes into `DeclaredClass`es.
pub fn into_loaded_classes(self) -> anyhow::Result<Vec<DeclaredClass>> {
use starknet_core::types::contract::{legacy::LegacyContractClass, SierraClass};

pub fn into_loaded_classes(self) -> Vec<DeclaredClass> {
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::<SierraClass>(&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::<LegacyContractClass>(&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()
}
Expand Down
71 changes: 29 additions & 42 deletions crates/client/devnet/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -52,30 +53,18 @@ 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 =
Felt::from_hex_unchecked("0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7");

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)]
Expand All @@ -88,30 +77,26 @@ pub struct ChainGenesisDescription {
}

impl ChainGenesisDescription {
pub fn base_config() -> Self {
Self {
pub fn base_config() -> anyhow::Result<Self> {
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<DevnetKeys> {
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", &[])
Expand All @@ -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);
Expand All @@ -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
Expand All @@ -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<UnverifiedFullBlock> {
Expand Down Expand Up @@ -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()
})
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);

// =====================================================================================
Expand Down Expand Up @@ -469,14 +455,15 @@ mod tests {
pubkey: pubkey.scalar(),
balance: account_balance,
address: calculated_address,
class_hash: account_class_hash,
};

let deploy_account_txn = BroadcastedDeployAccountTransaction::V3(BroadcastedDeployAccountTransactionV3 {
signature: vec![],
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 },
Expand Down
5 changes: 4 additions & 1 deletion crates/client/devnet/src/predeployed_contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<DevnetPredeployedContract>);
Expand Down Expand Up @@ -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::<anyhow::Result<_>>()?;
Expand All @@ -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(),
);
Expand Down
7 changes: 5 additions & 2 deletions crates/node/src/service/block_production.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down

0 comments on commit c232ebe

Please sign in to comment.