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

Implement deploy in execution layer #828

Merged
merged 27 commits into from
Oct 10, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
24b319b
Implement deploy in execution layer
Arcticae Oct 5, 2023
c1d97ea
Fix constructors error handling
Arcticae Oct 6, 2023
b1c47ed
Fix emitting events in constructor
Arcticae Oct 6, 2023
d3d6d76
Fix precalculation & tests, move salt incrementation.
Arcticae Oct 6, 2023
d2c3549
Merge branch 'master' into 805-deploy-in-execution-layer
Arcticae Oct 6, 2023
9c52222
Align hashes
Arcticae Oct 6, 2023
da55bcf
Merge branch 'master' into 805-deploy-in-execution-layer
Arcticae Oct 9, 2023
17df3de
Fix after master merge
Arcticae Oct 9, 2023
f2395ba
Fix after merge 2
Arcticae Oct 9, 2023
b0fa740
Fix resources tests, add a case with constructor
Arcticae Oct 9, 2023
7e6eda8
Merge branch 'master' into 805-deploy-in-execution-layer
Arcticae Oct 9, 2023
a180961
Add a check for `deploy_at` triggering the constructor
Arcticae Oct 9, 2023
18a416d
Add a test case checking for the correct constructor error handling f…
Arcticae Oct 9, 2023
a4cf21c
Remove accidental tokens
Arcticae Oct 9, 2023
341abab
Extract contract address to crate constants
Arcticae Oct 10, 2023
e6ece24
Fix conversions
Arcticae Oct 10, 2023
b2e84d6
Extract constant from forge to cheatnet
Arcticae Oct 10, 2023
c386f13
Try fix
Arcticae Oct 10, 2023
cdf85c5
Rearrange imports
Arcticae Oct 10, 2023
d91174f
Merge branch 'master' into 805-deploy-in-execution-layer
Arcticae Oct 10, 2023
4d82698
Update addresses
Arcticae Oct 10, 2023
5ec8489
Review suggestions
Arcticae Oct 10, 2023
7684105
lint
Arcticae Oct 10, 2023
e35b714
Merge branch 'master' into 805-deploy-in-execution-layer
Arcticae Oct 10, 2023
8069d51
Fix after merge
Arcticae Oct 10, 2023
715a4d6
Update CHANGELOG.md
Arcticae Oct 10, 2023
a206b39
Apply suggestions from code review
Arcticae Oct 10, 2023
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
227 changes: 66 additions & 161 deletions crates/cheatnet/src/cheatcodes/deploy.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
use crate::constants::TEST_ACCOUNT_CONTRACT_ADDRESS;
use crate::constants::{build_block_context, build_transaction_context};
use crate::state::BlockifierState;
use crate::{cheatcodes::EnhancedHintError, CheatnetState};
use crate::CheatnetState;
use anyhow::Result;
use blockifier::abi::abi_utils::selector_from_name;
use blockifier::execution::execution_utils::{felt_to_stark_felt, stark_felt_to_felt};
use blockifier::transaction::constants::EXECUTE_ENTRY_POINT_NAME;
use blockifier::abi::constants;
use blockifier::execution::entry_point::{
ConstructorContext, EntryPointExecutionContext, ExecutionResources,
};
use blockifier::execution::execution_utils::felt_to_stark_felt;
use std::sync::Arc;

use blockifier::state::state_api::State;
use cairo_felt::Felt252;
use cairo_lang_runner::short_string::as_cairo_short_string;
use cairo_vm::vm::errors::hint_errors::HintError::CustomHint;
use conversions::StarknetConversions;
use num_traits::ToPrimitive;

use starknet::core::utils::get_selector_from_name;
use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, PatriciaKey};
use starknet_api::hash::{StarkFelt, StarkHash};
use starknet_api::transaction::ContractAddressSalt;
use starknet_api::{patricia_key, stark_felt};
use crate::cheatcodes::EnhancedHintError;
use crate::execution::syscalls::execute_deployment;
use starknet_api::core::{ClassHash, ContractAddress};
use starknet_api::transaction::Calldata;

use super::CheatcodeError;
use crate::rpc::{call_contract, CallContractOutput, ResourceReport};
use crate::rpc::{CallContractFailure, ResourceReport};

#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone, PartialEq)]
pub struct DeployPayload {
pub contract_address: ContractAddress,
#[derive(Debug)]
pub struct DeployCallPayload {
pub resource_report: ResourceReport,
pub contract_address: ContractAddress,
}

#[allow(clippy::module_name_repetitions)]
Expand All @@ -35,15 +37,9 @@ pub fn deploy_at(
class_hash: &ClassHash,
calldata: &[Felt252],
contract_address: ContractAddress,
) -> Result<DeployPayload, CheatcodeError> {
let salt = cheatnet_state.get_salt();
cheatnet_state.increment_deploy_salt_base();

// Deploy a contract using syscall deploy.
let account_address = ContractAddress(patricia_key!(TEST_ACCOUNT_CONTRACT_ADDRESS));
let entry_point_selector = selector_from_name("deploy_contract");
) -> Result<DeployCallPayload, CheatcodeError> {
let blockifier_state_raw: &mut dyn State = blockifier_state.blockifier_state;

let blockifier_state_raw: &mut dyn State = blockifier_state.blockifier_state as &mut dyn State;
if let Ok(class_hash) = blockifier_state_raw.get_class_hash_at(contract_address) {
if class_hash != ClassHash::default() {
return Err(CheatcodeError::Unrecoverable(EnhancedHintError::from(
Expand All @@ -52,72 +48,56 @@ pub fn deploy_at(
}
}

let execute_calldata = create_execute_calldata(
calldata,
class_hash,
&account_address,
&entry_point_selector,
&salt,
let entry_point_execution_ctx = &mut EntryPointExecutionContext::new(
build_block_context(),
build_transaction_context(),
constants::MAX_STEPS_PER_TX,
);

let call_result = call_contract(
blockifier_state,
let ctor_context = ConstructorContext {
class_hash: *class_hash,
code_address: Some(contract_address),
storage_address: contract_address,
caller_address: Felt252::from(0x0000_1724_9872_3497_3219_3472_1083_7402_i128)
.to_contract_address(), // TODO: Extract to consts
};

let calldata = Calldata(Arc::new(
calldata.to_vec().iter().map(felt_to_stark_felt).collect(),
));

let resources = &mut ExecutionResources::default();

let result = execute_deployment(
blockifier_state_raw,
resources,
entry_point_execution_ctx,
ctor_context,
calldata,
u64::MAX,
cheatnet_state,
&account_address,
&get_selector_from_name(EXECUTE_ENTRY_POINT_NAME)
.unwrap()
.to_felt252(),
execute_calldata.as_slice(),
)
.unwrap_or_else(|err| panic!("Deploy txn failed: {err}"));

let blockifier_state_raw = &mut blockifier_state.blockifier_state;

match call_result {
CallContractOutput::Success {
resource_report, ..
} => {
let result = blockifier_state_raw
.set_class_hash_at(contract_address, *class_hash)
.map(|()| contract_address)
.map_err(|msg| {
CheatcodeError::Unrecoverable(EnhancedHintError::from(CustomHint(Box::from(
msg.to_string(),
))))
});

match result {
Ok(contract_address) => Ok(DeployPayload {
contract_address,
resource_report,
}),
Err(cheatcode_error) => Err(cheatcode_error),
}
}
CallContractOutput::Panic { panic_data, .. } => {
let panic_data_str = panic_data
.iter()
.map(|x| as_cairo_short_string(x).unwrap())
.collect::<Vec<String>>()
.join("\n");

for invalid_calldata_msg in [
"Failed to deserialize param #",
"Input too long for arguments",
] {
if panic_data_str.contains(invalid_calldata_msg) {
return Err(CheatcodeError::Unrecoverable(EnhancedHintError::from(
CustomHint(Box::from(panic_data_str)),
)));
}
}

Err(CheatcodeError::Recoverable(panic_data))
}
CallContractOutput::Error { msg, .. } => Err(CheatcodeError::Unrecoverable(
EnhancedHintError::from(CustomHint(Box::from(msg))),
)),
}
.map(|call_info| DeployCallPayload {
resource_report: ResourceReport::new(
call_info
.execution
.gas_consumed
.to_f64()
.expect("Conversion to f64 failed")
+ constants::DEPLOY_GAS_COST
.to_f64()
.expect("Conversion to f64 failed"),
resources,
),
contract_address,
})
.map_err(|err| {
let call_contract_failure =
CallContractFailure::from_execution_error(&err, &contract_address);
CheatcodeError::from(call_contract_failure)
});
cheatnet_state.increment_deploy_salt_base();
result
}

#[allow(clippy::module_name_repetitions)]
Expand All @@ -126,7 +106,7 @@ pub fn deploy(
cheatnet_state: &mut CheatnetState,
class_hash: &ClassHash,
calldata: &[Felt252],
) -> Result<DeployPayload, CheatcodeError> {
) -> Result<DeployCallPayload, CheatcodeError> {
let contract_address = cheatnet_state.precalculate_address(class_hash, calldata);

deploy_at(
Expand All @@ -137,78 +117,3 @@ pub fn deploy(
contract_address,
)
}

fn create_execute_calldata(
calldata: &[Felt252],
class_hash: &ClassHash,
account_address: &ContractAddress,
entry_point_selector: &EntryPointSelector,
salt: &ContractAddressSalt,
) -> Vec<Felt252> {
let calldata_len = u128::try_from(calldata.len()).unwrap();
let mut execute_calldata = vec![
*account_address.0.key(), // Contract address.
entry_point_selector.0, // EP selector.
stark_felt!(calldata_len + 3), // Calldata length.
class_hash.0, // Calldata: class_hash.
salt.0, // Contract_address_salt.
stark_felt!(calldata_len), // Constructor calldata length.
];
let mut calldata: Vec<StarkFelt> = calldata.iter().map(felt_to_stark_felt).collect();
execute_calldata.append(&mut calldata);
return execute_calldata
.iter()
.map(|sf| stark_felt_to_felt(*sf))
.collect();
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn execute_calldata() {
let calldata = create_execute_calldata(
&[Felt252::from(100), Felt252::from(200)],
&ClassHash(StarkFelt::from(123_u32)),
&ContractAddress::try_from(StarkFelt::from(111_u32)).unwrap(),
&EntryPointSelector(StarkFelt::from(222_u32)),
&ContractAddressSalt(StarkFelt::from(333_u32)),
);
assert_eq!(
calldata,
vec![
Felt252::from(111_u32),
Felt252::from(222_u32),
Felt252::from(5_u32),
Felt252::from(123_u32),
Felt252::from(333_u32),
Felt252::from(2_u32),
Felt252::from(100_u32),
Felt252::from(200_u32),
]
);
}

#[test]
fn execute_calldata_no_entrypoint_calldata() {
let calldata = create_execute_calldata(
&[],
&ClassHash(StarkFelt::from(123_u32)),
&ContractAddress::try_from(StarkFelt::from(111_u32)).unwrap(),
&EntryPointSelector(StarkFelt::from(222_u32)),
&ContractAddressSalt(StarkFelt::from(333_u32)),
);
assert_eq!(
calldata,
vec![
Felt252::from(111_u32),
Felt252::from(222_u32),
Felt252::from(3_u32),
Felt252::from(123_u32),
Felt252::from(333_u32),
Felt252::from(0_u32),
]
);
}
}
13 changes: 13 additions & 0 deletions crates/cheatnet/src/cheatcodes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::io;

use crate::rpc::CallContractFailure;
use blockifier::state::errors::StateError;
use cairo_felt::Felt252;
use cairo_vm::vm::errors::hint_errors::HintError;
Expand Down Expand Up @@ -53,6 +54,7 @@ impl From<EnhancedHintError> for HintError {
}
}

/// A structure used for returning cheatcode errors in tests
#[derive(Debug)]
pub enum CheatcodeError {
Recoverable(Vec<Felt252>), // Return error result in cairo
Expand All @@ -65,6 +67,17 @@ impl From<EnhancedHintError> for CheatcodeError {
}
}

impl From<CallContractFailure> for CheatcodeError {
fn from(value: CallContractFailure) -> Self {
match value {
CallContractFailure::Panic { panic_data } => CheatcodeError::Recoverable(panic_data),
CallContractFailure::Error { msg } => {
CheatcodeError::Unrecoverable(HintError::CustomHint(Box::from(msg)).into())
}
}
}
}

#[derive(Debug, PartialEq, Clone)]
pub struct ContractArtifacts {
pub sierra: String,
Expand Down
12 changes: 5 additions & 7 deletions crates/cheatnet/src/cheatcodes/precalculate_address.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
use crate::constants::TEST_ACCOUNT_CONTRACT_ADDRESS;

use crate::CheatnetState;
use blockifier::execution::execution_utils::felt_to_stark_felt;
use cairo_felt::Felt252;
use starknet_api::core::PatriciaKey;
use starknet_api::core::{calculate_contract_address, ClassHash, ContractAddress};
use starknet_api::hash::{StarkFelt, StarkHash};
use starknet_api::hash::StarkFelt;
use starknet_api::transaction::Calldata;

use starknet_api::patricia_key;
use conversions::StarknetConversions;

impl CheatnetState {
#[must_use]
Expand All @@ -17,11 +14,12 @@ impl CheatnetState {
class_hash: &ClassHash,
calldata: &[Felt252],
) -> ContractAddress {
let account_address = ContractAddress(patricia_key!(TEST_ACCOUNT_CONTRACT_ADDRESS));
let salt = self.get_salt();

let execute_calldata = create_execute_calldata(calldata);
calculate_contract_address(salt, *class_hash, &execute_calldata, account_address).unwrap()
let deployer_address =
Felt252::from(0x0000_1724_9872_3497_3219_3472_1083_7402_i128).to_contract_address();
calculate_contract_address(salt, *class_hash, &execute_calldata, deployer_address).unwrap()
}
}

Expand Down
Loading