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

Issue #1502 add a name field in GenesisClassJson (Unfinished) #1552

Closed
wants to merge 3 commits into from
Closed
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
134 changes: 110 additions & 24 deletions crates/katana/primitives/src/genesis/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
use base64::prelude::*;
use cairo_lang_starknet_classes::casm_contract_class::StarknetSierraCompilationError;
use cairo_vm::types::errors::program_errors::ProgramError;
use rayon::prelude::*;
use serde::de::value::MapAccessDeserializer;
use serde::de::Visitor;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -97,6 +96,15 @@
/// The class hash of the contract. If not provided, the class hash is computed from the
/// class at `path`.
pub class_hash: Option<ClassHash>,
// Allows class identification by a unique name rather than by hash
name: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum NameOrHash {
ClassName(String),
ClassHash(ClassHash),
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
Expand All @@ -108,7 +116,7 @@
pub decimals: u8,
/// The class hash of the fee token contract.
/// If not provided, the default fee token class is used.
pub class: Option<ClassHash>,
pub class: Option<NameOrHash>,
/// To initialize the fee token contract storage
pub storage: Option<HashMap<StorageKey, StorageValue>>,
}
Expand All @@ -120,15 +128,15 @@
pub address: Option<ContractAddress>,
/// The class hash of the universal deployer contract.
/// If not provided, the default UD class is used.
pub class: Option<ClassHash>,
pub class: Option<NameOrHash>,
/// To initialize the UD contract storage
pub storage: Option<HashMap<StorageKey, StorageValue>>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct GenesisContractJson {
pub class: Option<ClassHash>,
pub class: Option<NameOrHash>,
pub balance: Option<U256>,
pub nonce: Option<FieldElement>,
pub storage: Option<HashMap<StorageKey, StorageValue>>,
Expand Down Expand Up @@ -182,6 +190,15 @@
#[error(transparent)]
Decode(#[from] base64::DecodeError),

#[error("Class name {0} already exists in the genesis classes")]
DuplicateClassName(String),

#[error("Class name not found in the genesis classes: {0}")]
InvalidClassName(String),

#[error("Class hash was not provided for a class")]
MissingClassHash,

#[error(transparent)]
Other(#[from] anyhow::Error),
}
Expand Down Expand Up @@ -263,18 +280,25 @@
type Error = GenesisJsonError;

fn try_from(value: GenesisJson) -> Result<Self, Self::Error> {
let mut classes: HashMap<ClassHash, GenesisClass> = value
.classes
.into_par_iter()
.map(|entry| {
let GenesisClassJson { class, class_hash } = entry;

let mut name_to_class_hash: HashMap<String, starknet::core::types::FieldElement> =
HashMap::new();
let mut classes: HashMap<ClassHash, GenesisClass> = HashMap::new();
for entry in value.classes.into_iter() {
let GenesisClassJson { class, class_hash, name } = entry;
let artifact = match class {
PathOrFullArtifact::Artifact(artifact) => artifact,
PathOrFullArtifact::Path(path) => {
return Err(GenesisJsonError::UnresolvedClassPath(path));
}
};
// Insert the name and class_hash into the name_to_class_hash HashMap
if let Some(name) = name {
if let Some(class_hash) = class_hash {
name_to_class_hash.insert(name, class_hash);
} else {
return Err(GenesisJsonError::MissingClassHash);

Check warning on line 299 in crates/katana/primitives/src/genesis/json.rs

View check run for this annotation

Codecov / codecov/patch

crates/katana/primitives/src/genesis/json.rs#L299

Added line #L299 was not covered by tests
}
}

let sierra = serde_json::from_value::<SierraClass>(artifact.clone());

Expand Down Expand Up @@ -311,26 +335,40 @@
}
};

Ok((class_hash, GenesisClass { compiled_class_hash, sierra, casm }))
})
.collect::<Result<_, GenesisJsonError>>()?;
classes.insert(class_hash, GenesisClass { compiled_class_hash, sierra, casm });
}

let fee_token = FeeTokenConfig {
name: value.fee_token.name,
symbol: value.fee_token.symbol,
decimals: value.fee_token.decimals,
address: value.fee_token.address.unwrap_or(DEFAULT_FEE_TOKEN_ADDRESS),
class_hash: value.fee_token.class.unwrap_or(DEFAULT_LEGACY_ERC20_CONTRACT_CLASS_HASH),
class_hash: match value.fee_token.class {
Some(NameOrHash::ClassHash(class_hash)) => class_hash,

Check warning on line 347 in crates/katana/primitives/src/genesis/json.rs

View check run for this annotation

Codecov / codecov/patch

crates/katana/primitives/src/genesis/json.rs#L347

Added line #L347 was not covered by tests
Some(NameOrHash::ClassName(ref class_name)) => *name_to_class_hash
.get(class_name)
.ok_or_else(|| GenesisJsonError::InvalidClassName(class_name.clone()))?,
None => DEFAULT_LEGACY_ERC20_CONTRACT_CLASS_HASH,
},
storage: value.fee_token.storage,
};

match value.fee_token.class {
Some(hash) => {
Some(NameOrHash::ClassHash(hash)) => {

Check warning on line 357 in crates/katana/primitives/src/genesis/json.rs

View check run for this annotation

Codecov / codecov/patch

crates/katana/primitives/src/genesis/json.rs#L357

Added line #L357 was not covered by tests
if !classes.contains_key(&hash) {
return Err(GenesisJsonError::MissingClass(hash));
}
}

Some(NameOrHash::ClassName(name)) => {
let hash = name_to_class_hash
.get(&name)
.cloned()
.ok_or_else(|| GenesisJsonError::InvalidClassName(name.clone()))?;
if !classes.contains_key(&hash) {
return Err(GenesisJsonError::MissingClass(hash));

Check warning on line 369 in crates/katana/primitives/src/genesis/json.rs

View check run for this annotation

Codecov / codecov/patch

crates/katana/primitives/src/genesis/json.rs#L369

Added line #L369 was not covered by tests
}
}
// if no class hash is provided, use the default fee token class
None => {
let _ = classes.insert(
Expand All @@ -346,7 +384,22 @@

let universal_deployer = if let Some(config) = value.universal_deployer {
match config.class {
Some(hash) => {
Some(NameOrHash::ClassHash(hash)) => {
if !classes.contains_key(&hash) {
return Err(GenesisJsonError::MissingClass(hash));
}

Some(UniversalDeployerConfig {
class_hash: hash,
address: config.address.unwrap_or(DEFAULT_UDC_ADDRESS),
storage: config.storage,
})

Check warning on line 396 in crates/katana/primitives/src/genesis/json.rs

View check run for this annotation

Codecov / codecov/patch

crates/katana/primitives/src/genesis/json.rs#L387-L396

Added lines #L387 - L396 were not covered by tests
}

Some(NameOrHash::ClassName(name)) => {
let hash = *name_to_class_hash
.get(&name)
.ok_or_else(|| GenesisJsonError::InvalidClassName(name))?;

Check warning on line 402 in crates/katana/primitives/src/genesis/json.rs

View check run for this annotation

Codecov / codecov/patch

crates/katana/primitives/src/genesis/json.rs#L399-L402

Added lines #L399 - L402 were not covered by tests
if !classes.contains_key(&hash) {
return Err(GenesisJsonError::MissingClass(hash));
}
Expand Down Expand Up @@ -444,7 +497,20 @@

for (address, contract) in value.contracts {
// check that the class hash exists in the classes field
if let Some(hash) = contract.class {
let class_hash = if let Some(hash) = contract.class {
Some(match hash {
NameOrHash::ClassHash(hash) => hash,

Check warning on line 502 in crates/katana/primitives/src/genesis/json.rs

View check run for this annotation

Codecov / codecov/patch

crates/katana/primitives/src/genesis/json.rs#L502

Added line #L502 was not covered by tests
NameOrHash::ClassName(name) => {
// Handle the case when the class is specified by name.
*name_to_class_hash
.get(&name)
.ok_or_else(|| GenesisJsonError::InvalidClassName(name))?
}
})
} else {
None
};
if let Some(hash) = class_hash {
if !classes.contains_key(&hash) {
return Err(GenesisJsonError::MissingClass(hash));
}
Expand All @@ -454,7 +520,7 @@
address,
GenesisAllocation::Contract(GenesisContractAlloc {
balance: contract.balance,
class_hash: contract.class,
class_hash,
nonce: contract.nonce,
storage: contract.storage,
}),
Expand Down Expand Up @@ -540,6 +606,7 @@
use std::io::BufReader;
use std::path::PathBuf;
use std::str::FromStr;
use crate::genesis::json::NameOrHash;

use alloy_primitives::U256;
use starknet::macros::felt;
Expand Down Expand Up @@ -579,7 +646,7 @@
assert_eq!(json.fee_token.address, Some(ContractAddress::from(felt!("0x55"))));
assert_eq!(json.fee_token.name, String::from("ETHER"));
assert_eq!(json.fee_token.symbol, String::from("ETH"));
assert_eq!(json.fee_token.class, Some(felt!("0x8")));
assert_eq!(json.fee_token.class, Some(NameOrHash::ClassName(String::from("MyErc20"))));
assert_eq!(json.fee_token.decimals, 18);
assert_eq!(
json.fee_token.storage,
Expand Down Expand Up @@ -667,7 +734,10 @@
Some(U256::from_str("0xD3C21BCECCEDA1000000").unwrap())
);
assert_eq!(json.contracts[&contract_1].nonce, None);
assert_eq!(json.contracts[&contract_1].class, Some(felt!("0x8")));
assert_eq!(
json.contracts[&contract_1].class,
Some(NameOrHash::ClassName(String::from("MyErc20")))
);
assert_eq!(
json.contracts[&contract_1].storage,
Some(HashMap::from([(felt!("0x1"), felt!("0x1")), (felt!("0x2"), felt!("0x2"))]))
Expand Down Expand Up @@ -695,15 +765,20 @@
GenesisClassJson {
class_hash: Some(felt!("0x8")),
class: PathBuf::from("../../../contracts/compiled/erc20.json").into(),
name: Some("MyErc20".to_string()),
},
GenesisClassJson {
class_hash: Some(felt!("0x80085")),
class: PathBuf::from("../../../contracts/compiled/universal_deployer.json")
.into(),
name:None,

},
GenesisClassJson {
class_hash: Some(felt!("0xa55")),
class: PathBuf::from("../../../contracts/compiled/oz_account_080.json").into(),
name:None,

},
]
);
Expand All @@ -712,28 +787,39 @@
#[test]
fn deserialize_from_json_with_class() {
let file = File::open("./src/genesis/test-genesis-with-class.json").unwrap();
let genesis: GenesisJson = serde_json::from_reader(BufReader::new(file)).unwrap();

let genesis_result: Result<GenesisJson, _> = serde_json::from_reader(BufReader::new(file));
match genesis_result {
Ok(genesis) => {

Check warning on line 792 in crates/katana/primitives/src/genesis/json.rs

View check run for this annotation

Codecov / codecov/patch

crates/katana/primitives/src/genesis/json.rs#L792

Added line #L792 was not covered by tests
assert_eq!(
genesis.classes,
vec![
GenesisClassJson {
class_hash: Some(felt!("0x8")),
class_hash: None,

Check warning on line 797 in crates/katana/primitives/src/genesis/json.rs

View check run for this annotation

Codecov / codecov/patch

crates/katana/primitives/src/genesis/json.rs#L797

Added line #L797 was not covered by tests
class: PathBuf::from("../../../contracts/compiled/erc20.json").into(),
name: Some("MyErc20".to_string()),

Check warning on line 799 in crates/katana/primitives/src/genesis/json.rs

View check run for this annotation

Codecov / codecov/patch

crates/katana/primitives/src/genesis/json.rs#L799

Added line #L799 was not covered by tests
},
GenesisClassJson {
class_hash: Some(felt!("0x80085")),
class: PathBuf::from("../../../contracts/compiled/universal_deployer.json")
.into(),
name: None,

Check warning on line 805 in crates/katana/primitives/src/genesis/json.rs

View check run for this annotation

Codecov / codecov/patch

crates/katana/primitives/src/genesis/json.rs#L805

Added line #L805 was not covered by tests
},
GenesisClassJson {
class_hash: Some(felt!("0xa55")),
class: serde_json::to_value(DEFAULT_OZ_ACCOUNT_CONTRACT.clone())
.unwrap()
.into(),
name: None,

Check warning on line 812 in crates/katana/primitives/src/genesis/json.rs

View check run for this annotation

Codecov / codecov/patch

crates/katana/primitives/src/genesis/json.rs#L812

Added line #L812 was not covered by tests
},
]
);
);

Check warning on line 815 in crates/katana/primitives/src/genesis/json.rs

View check run for this annotation

Codecov / codecov/patch

crates/katana/primitives/src/genesis/json.rs#L815

Added line #L815 was not covered by tests
},
Err(e) => {
println!("Error parsing JSON: {:?}", e);
}
}


}

#[test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"classes": [
{
"class": "../../../contracts/compiled/erc20.json",
"classHash": "0x8"
"classHash": "MyErc20"
},
{
"class": "../../../contracts/compiled/universal_deployer.json",
Expand Down
7 changes: 4 additions & 3 deletions crates/katana/primitives/src/genesis/test-genesis.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"name": "ETHER",
"symbol": "ETH",
"decimals": 18,
"class": "0x8",
"class": "MyErc20",
"storage": {
"0x111": "0x1",
"0x222": "0x2"
Expand Down Expand Up @@ -52,7 +52,7 @@
"contracts": {
"0x29873c310fbefde666dc32a1554fea6bb45eecc84f680f8a2b0a8fbb8cb89af": {
"balance": "0xD3C21BCECCEDA1000000",
"class": "0x8",
"class": "MyErc20",
"storage": {
"0x1": "0x1",
"0x2": "0x2"
Expand All @@ -70,7 +70,8 @@
"classes": [
{
"class": "../../../contracts/compiled/erc20.json",
"classHash": "0x8"
"classHash": "0x8",
"name": "MyErc20"
},
{
"class": "../../../contracts/compiled/universal_deployer.json",
Expand Down
Loading