Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

Commit

Permalink
Add new constructor declare v2 (#823)
Browse files Browse the repository at this point in the history
* Add casm class hash

* add casm class hash check

* fix tests

* Update src/lib.rs

Co-authored-by: Mario Rugiero <mrugiero@gmail.com>

* Add casm contract class as optional

* Add test with casm contract class

* update doc

* Update src/lib.rs

Co-authored-by: Estéfano Bargas <estefano.bargas@fing.edu.uy>

* Update src/lib.rs

Co-authored-by: Estéfano Bargas <estefano.bargas@fing.edu.uy>

* Add missed import

* fix test

* add tests

---------

Co-authored-by: Mario Rugiero <mrugiero@gmail.com>
Co-authored-by: Estéfano Bargas <estefano.bargas@fing.edu.uy>
  • Loading branch information
3 people authored Jul 17, 2023
1 parent ae06254 commit a66205b
Show file tree
Hide file tree
Showing 3 changed files with 234 additions and 14 deletions.
12 changes: 10 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1004,13 +1004,21 @@ mod test {
fn test_declare_v2_with_invalid_compiled_class_hash() {
let (block_context, mut state) = create_account_tx_test_state().unwrap();
let mut declare_v2 = declarev2_tx();
declare_v2.compiled_class_hash = Felt252::from(1);
let real_casm_class_hash = declare_v2.compiled_class_hash;
let wrong_casm_class_hash = Felt252::from(1);
declare_v2.compiled_class_hash = wrong_casm_class_hash.clone();
let declare_tx = Transaction::DeclareV2(Box::new(declare_v2));

let err = declare_tx
.execute(&mut state, &block_context, INITIAL_GAS_COST)
.unwrap_err();

assert_eq!(err.to_string(), "Invalid compiled class, expected class hash: \"1948962768849191111780391610229754715773924969841143100991524171924131413970\", but received: \"1\"".to_string());
assert_eq!(
err.to_string(),
format!(
"Invalid compiled class, expected class hash: {}, but received: {}",
real_casm_class_hash, wrong_casm_class_hash
)
);
}
}
234 changes: 223 additions & 11 deletions src/transaction/declare_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub struct DeclareV2 {
pub sierra_contract_class: SierraContractClass,
pub sierra_class_hash: Felt252,
pub hash_value: Felt252,
pub casm_class: once_cell::unsync::OnceCell<CasmContractClass>,
pub casm_class: Option<CasmContractClass>,
pub skip_validate: bool,
pub skip_execute: bool,
pub skip_fee_transfer: bool,
Expand All @@ -56,6 +56,7 @@ impl DeclareV2 {
/// It will calculate the sierra class hash and the transaction hash.
/// ## Parameters:
/// - sierra_contract_class: The sierra contract class of the contract to declare
/// - casm_contract_class: The casm contract class of the contract to declare. This is optional.
/// - compiled_class_hash: the class hash of the contract compiled with Cairo1 or newer.
/// - chain_id: Id of the network where is going to be declare, those can be: Mainnet, Testnet.
/// - sender_address: The address of the account declaring the contract.
Expand All @@ -66,6 +67,7 @@ impl DeclareV2 {
#[allow(clippy::too_many_arguments)]
pub fn new(
sierra_contract_class: &SierraContractClass,
casm_contract_class: Option<CasmContractClass>,
compiled_class_hash: Felt252,
chain_id: Felt252,
sender_address: Address,
Expand All @@ -89,6 +91,7 @@ impl DeclareV2 {
Self::new_with_sierra_class_hash_and_tx_hash(
sierra_contract_class,
sierra_class_hash,
casm_contract_class,
compiled_class_hash,
sender_address,
max_fee,
Expand All @@ -103,6 +106,7 @@ impl DeclareV2 {
/// ## Parameters:
/// - sierra_contract_class: The sierra contract class of the contract to declare
/// - sierra_class_hash: The precomputed hash for the sierra contract
/// - casm_contract_class: The casm contract class of the contract to declare. This is optional.
/// - compiled_class_hash: the class hash of the contract compiled with Cairo1 or newer.
/// - sender_address: The address of the account declaring the contract.
/// - max_fee: refers to max amount of fee that a declare takes.
Expand All @@ -116,6 +120,7 @@ impl DeclareV2 {
pub fn new_with_sierra_class_hash_and_tx_hash(
sierra_contract_class: &SierraContractClass,
sierra_class_hash: Felt252,
casm_contract_class: Option<CasmContractClass>,
compiled_class_hash: Felt252,
sender_address: Address,
max_fee: u128,
Expand All @@ -138,7 +143,7 @@ impl DeclareV2 {
nonce,
compiled_class_hash,
hash_value,
casm_class: Default::default(),
casm_class: casm_contract_class,
skip_execute: false,
skip_validate: false,
skip_fee_transfer: false,
Expand All @@ -156,7 +161,8 @@ impl DeclareV2 {

// creates a new instance of a declare but without the computation of the transaction hash.
/// ## Parameters:
/// - sierra_contract_class: The sierra contract class of the contract to declare
/// - sierra_contract_class: The sierra contract class of the contract to declare.
/// - casm_contract_class: The casm contract class of the contract to declare. This is optional.
/// - compiled_class_hash: the class hash of the contract compiled with Cairo1 or newer.
/// - sender_address: The address of the account declaring the contract.
/// - max_fee: refers to max amount of fee that a declare takes.
Expand All @@ -167,6 +173,7 @@ impl DeclareV2 {
#[allow(clippy::too_many_arguments)]
pub fn new_with_tx_hash(
sierra_contract_class: &SierraContractClass,
casm_contract_class: Option<CasmContractClass>,
compiled_class_hash: Felt252,
sender_address: Address,
max_fee: u128,
Expand All @@ -180,6 +187,7 @@ impl DeclareV2 {
Self::new_with_sierra_class_hash_and_tx_hash(
sierra_contract_class,
sierra_class_hash,
casm_contract_class,
compiled_class_hash,
sender_address,
max_fee,
Expand All @@ -194,6 +202,7 @@ impl DeclareV2 {
/// ## Parameters:
/// - sierra_contract_class: The sierra contract class of the contract to declare
/// - sierra_class_hash: The precomputed hash for the sierra contract
/// - casm_contract_class: The casm contract class of the contract to declare. This is optional.
/// - compiled_class_hash: the class hash of the contract compiled with Cairo1 or newer.
/// - chain_id: Id of the network where is going to be declare, those can be: Mainnet, Testnet.
/// - sender_address: The address of the account declaring the contract.
Expand All @@ -205,6 +214,7 @@ impl DeclareV2 {
pub fn new_with_sierra_class_hash(
sierra_contract_class: &SierraContractClass,
sierra_class_hash: Felt252,
casm_contract_class: Option<CasmContractClass>,
compiled_class_hash: Felt252,
chain_id: Felt252,
sender_address: Address,
Expand All @@ -226,6 +236,7 @@ impl DeclareV2 {
Self::new_with_sierra_class_hash_and_tx_hash(
sierra_contract_class,
sierra_class_hash,
casm_contract_class,
compiled_class_hash,
sender_address,
max_fee,
Expand Down Expand Up @@ -374,22 +385,23 @@ impl DeclareV2 {
&self,
state: &mut S,
) -> Result<(), TransactionError> {
let casm_class = self
.casm_class
.get_or_try_init(|| {
let casm_class = match &self.casm_class {
None => {
CasmContractClass::from_contract_class(self.sierra_contract_class.clone(), true)
})
.map_err(|e| TransactionError::SierraCompileError(e.to_string()))?;
.map_err(|e| TransactionError::SierraCompileError(e.to_string()))?
}
Some(casm_contract_class) => casm_contract_class.clone(),
};

let casm_class_hash = compute_casm_class_hash(casm_class)?;
let casm_class_hash = compute_casm_class_hash(&casm_class)?;
if casm_class_hash != self.compiled_class_hash {
return Err(TransactionError::InvalidCompiledClassHash(
casm_class_hash.to_string(),
self.compiled_class_hash.to_string(),
));
}
state.set_compiled_class_hash(&self.sierra_class_hash, &self.compiled_class_hash)?;
state.set_compiled_class(&self.compiled_class_hash, casm_class.clone())?;
state.set_compiled_class(&self.compiled_class_hash, casm_class)?;

Ok(())
}
Expand Down Expand Up @@ -473,7 +485,7 @@ mod tests {
use num_traits::{One, Zero};

#[test]
fn create_declare_v2_test() {
fn create_declare_v2_without_casm_contract_class_test() {
// read file to create sierra contract class
let version;
let path;
Expand Down Expand Up @@ -502,6 +514,76 @@ mod tests {

let internal_declare = DeclareV2::new_with_tx_hash(
&sierra_contract_class,
None,
casm_class_hash,
sender_address,
0,
version,
[1.into()].to_vec(),
Felt252::zero(),
Felt252::one(),
)
.unwrap();

// crate state to store casm contract class
let casm_contract_class_cache = HashMap::new();
let state_reader = InMemoryStateReader::default();
let mut state = CachedState::new(state_reader, None, Some(casm_contract_class_cache));

// call compile and store
assert!(internal_declare
.compile_and_store_casm_class(&mut state)
.is_ok());

// test we can retreive the data
let expected_casm_class = CasmContractClass::from_contract_class(
internal_declare.sierra_contract_class.clone(),
true,
)
.unwrap();

let casm_class = match state
.get_contract_class(&internal_declare.compiled_class_hash.to_be_bytes())
.unwrap()
{
CompiledClass::Casm(casm) => *casm,
_ => unreachable!(),
};

assert_eq!(expected_casm_class, casm_class);
}

#[test]
fn create_declare_v2_with_casm_contract_class_test() {
// read file to create sierra contract class
let version;
let path;
#[cfg(not(feature = "cairo_1_tests"))]
{
version = Felt252::from(2);
path = PathBuf::from("starknet_programs/cairo2/fibonacci.sierra");
}

#[cfg(feature = "cairo_1_tests")]
{
version = Felt252::from(1);
path = PathBuf::from("starknet_programs/cairo1/fibonacci.sierra");
}

let file = File::open(path).unwrap();
let reader = BufReader::new(file);
let sierra_contract_class: cairo_lang_starknet::contract_class::ContractClass =
serde_json::from_reader(reader).unwrap();
let sender_address = Address(1.into());
let casm_class =
CasmContractClass::from_contract_class(sierra_contract_class.clone(), true).unwrap();
let casm_class_hash = compute_casm_class_hash(&casm_class).unwrap();

// create internal declare v2

let internal_declare = DeclareV2::new_with_tx_hash(
&sierra_contract_class,
Some(casm_class),
casm_class_hash,
sender_address,
0,
Expand Down Expand Up @@ -572,6 +654,7 @@ mod tests {
let internal_declare = DeclareV2::new_with_sierra_class_hash_and_tx_hash(
&sierra_contract_class,
sierra_class_hash,
Some(casm_class),
casm_class_hash,
sender_address,
0,
Expand Down Expand Up @@ -609,4 +692,133 @@ mod tests {

assert_eq!(expected_casm_class, casm_class);
}

#[test]
fn create_declare_v2_with_casm_contract_class_none_test() {
// read file to create sierra contract class
let version;
let path;
#[cfg(not(feature = "cairo_1_tests"))]
{
version = Felt252::from(2);
path = PathBuf::from("starknet_programs/cairo2/fibonacci.sierra");
}

#[cfg(feature = "cairo_1_tests")]
{
version = Felt252::from(1);
path = PathBuf::from("starknet_programs/cairo1/fibonacci.sierra");
}

let file = File::open(path).unwrap();
let reader = BufReader::new(file);
let sierra_contract_class: cairo_lang_starknet::contract_class::ContractClass =
serde_json::from_reader(reader).unwrap();
let sender_address = Address(1.into());
let casm_class =
CasmContractClass::from_contract_class(sierra_contract_class.clone(), true).unwrap();
let casm_class_hash = compute_casm_class_hash(&casm_class).unwrap();

// create internal declare v2

let internal_declare = DeclareV2::new_with_tx_hash(
&sierra_contract_class,
None,
casm_class_hash,
sender_address,
0,
version,
[1.into()].to_vec(),
Felt252::zero(),
Felt252::one(),
)
.unwrap();

// crate state to store casm contract class
let casm_contract_class_cache = HashMap::new();
let state_reader = InMemoryStateReader::default();
let mut state = CachedState::new(state_reader, None, Some(casm_contract_class_cache));

// call compile and store
assert!(internal_declare
.compile_and_store_casm_class(&mut state)
.is_ok());

// test we can retreive the data
let expected_casm_class = CasmContractClass::from_contract_class(
internal_declare.sierra_contract_class.clone(),
true,
)
.unwrap();

let casm_class = match state
.get_contract_class(&internal_declare.compiled_class_hash.to_be_bytes())
.unwrap()
{
CompiledClass::Casm(casm) => *casm,
_ => unreachable!(),
};

assert_eq!(expected_casm_class, casm_class);
}

#[test]
fn create_declare_v2_wrong_casm_class_hash_test() {
// read file to create sierra contract class
let version;
let path;
#[cfg(not(feature = "cairo_1_tests"))]
{
version = Felt252::from(2);
path = PathBuf::from("starknet_programs/cairo2/fibonacci.sierra");
}

#[cfg(feature = "cairo_1_tests")]
{
version = Felt252::from(1);
path = PathBuf::from("starknet_programs/cairo1/fibonacci.sierra");
}

let file = File::open(path).unwrap();
let reader = BufReader::new(file);
let sierra_contract_class: cairo_lang_starknet::contract_class::ContractClass =
serde_json::from_reader(reader).unwrap();
let sender_address = Address(1.into());
let casm_class =
CasmContractClass::from_contract_class(sierra_contract_class.clone(), true).unwrap();
let casm_class_hash = compute_casm_class_hash(&casm_class).unwrap();

let sended_class_hash = Felt252::from(5);
// create internal declare v2

let internal_declare = DeclareV2::new_with_tx_hash(
&sierra_contract_class,
None,
sended_class_hash.clone(),
sender_address,
0,
version,
[1.into()].to_vec(),
Felt252::zero(),
Felt252::one(),
)
.unwrap();

// crate state to store casm contract class
let casm_contract_class_cache = HashMap::new();
let state_reader = InMemoryStateReader::default();
let mut state = CachedState::new(state_reader, None, Some(casm_contract_class_cache));

let expected_err = format!(
"Invalid compiled class, expected class hash: {}, but received: {}",
casm_class_hash, sended_class_hash
);
assert_eq!(
internal_declare
.compile_and_store_casm_class(&mut state)
.unwrap_err()
.to_string(),
expected_err
);
}
}
2 changes: 1 addition & 1 deletion src/transaction/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,6 @@ pub enum TransactionError {
CallInfoIsNone,
#[error("Unsupported version {0:?}")]
UnsupportedVersion(String),
#[error("Invalid compiled class, expected class hash: {0:?}, but received: {1:?}")]
#[error("Invalid compiled class, expected class hash: {0}, but received: {1}")]
InvalidCompiledClassHash(String, String),
}

0 comments on commit a66205b

Please # to comment.