Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: calculate class hash in devnet #269

Merged
merged 5 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just out of curiosity, why aren’t the classes definition stored compressed directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't understand the question sorry, the compressed version is stored now right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's just that we produce uncompressed class definitions with scarb

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok, I am not really sure but if Scarb has that option then this could be an optimisation, yes.

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
Loading