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

Precalculate contract address function #433

Merged
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e8d9260
change how works cheatcode declare and deploy + fix tests
tkumor3 Aug 8, 2023
3b03d8f
adds next integration tests
tkumor3 Aug 8, 2023
dece3b2
fix test
tkumor3 Aug 8, 2023
d747489
adds possibilities to precalculate contract address before deploy
tkumor3 Aug 10, 2023
7cc957e
adds deploy counter to cheatedState
tkumor3 Aug 10, 2023
9e5d477
adds integration test
tkumor3 Aug 10, 2023
ff2f279
rename tests
tkumor3 Aug 10, 2023
92e826f
fix
tkumor3 Aug 10, 2023
ee3c1f1
fix
tkumor3 Aug 10, 2023
55ba3e9
feedback
tkumor3 Aug 10, 2023
69337b4
feedback
tkumor3 Aug 10, 2023
503d37d
feedback
tkumor3 Aug 11, 2023
3daebea
feedback
tkumor3 Aug 11, 2023
2062766
add missing file
tkumor3 Aug 11, 2023
2461804
revert simple test
tkumor3 Aug 11, 2023
985f4bb
adds docs
tkumor3 Aug 11, 2023
c3cb7a4
feedback
tkumor3 Aug 11, 2023
5c90cf8
fix
tkumor3 Aug 11, 2023
8563a83
Merge branch 'master' into 395-support-for-precalculating-contract-ad…
tkumor3 Aug 16, 2023
f33ab1b
adds missing changes
tkumor3 Aug 16, 2023
df16b39
update docs
tkumor3 Aug 16, 2023
1b2d262
fix lint
tkumor3 Aug 16, 2023
1320f30
fix lineter
tkumor3 Aug 16, 2023
64a9203
fix docs
tkumor3 Aug 16, 2023
77479df
fix
tkumor3 Aug 16, 2023
6f52853
ignor e2e test
tkumor3 Aug 16, 2023
600f9f1
fix docs warning
tkumor3 Aug 16, 2023
3d08554
Merge branch 'master' into 395-support-for-precalculating-contract-ad…
tkumor3 Aug 17, 2023
bc313e3
fix tests
tkumor3 Aug 17, 2023
b07a3cd
Merge branch 'master' into 395-support-for-precalculating-contract-ad…
tkumor3 Aug 17, 2023
065eab2
refactor
tkumor3 Aug 17, 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
12 changes: 6 additions & 6 deletions benchmarks/data/forge.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ use option::OptionTrait;
use traits::TryInto;
use starknet::ContractAddress;
use starknet::Felt252TryIntoContractAddress;
use cheatcodes::PreparedContract;

use project::IHelloStarknetSafeDispatcher;
use project::IHelloStarknetSafeDispatcherTrait;

use cheatcodes::ContractClass;
use cheatcodes::ContractClassTrait;
tkumor3 marked this conversation as resolved.
Show resolved Hide resolved

fn deploy_hello_starknet() -> ContractAddress {
let class_hash = declare('HelloStarknet').unwrap();
let prepared = PreparedContract {
contract_address: 1234, class_hash: class_hash, constructor_calldata: @ArrayTrait::new()
};
let contract_address = deploy(prepared).unwrap();
let contract = declare('HelloStarknet').unwrap();
let constructor_calldata = @ArrayTrait::new()
let contract_address = contract.deploy(constructor_calldata).unwrap();

let contract_address: ContractAddress = contract_address.try_into().unwrap();

Expand Down
9 changes: 3 additions & 6 deletions benchmarks/data/protostar.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@ use option::OptionTrait;
use traits::TryInto;
use starknet::ContractAddress;
use starknet::Felt252TryIntoContractAddress;
use cheatcodes::PreparedContract;

fn deploy_hello_starknet() -> felt252 {
let class_hash = declare('HelloStarknet').unwrap();
let prepared = PreparedContract {
contract_address: 1234, class_hash: class_hash, constructor_calldata: @ArrayTrait::new()
};
let contract_address = deploy(prepared).unwrap();
let contract = declare('HelloStarknet');

let contract_address = contract.deploy(@ArrayTrait::new()).unwrap();

contract_address
}
Expand Down
95 changes: 59 additions & 36 deletions cheatcodes/src/cheatcodes.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@ use starknet::Felt252TryIntoClassHash;
use starknet::Felt252TryIntoContractAddress;

#[derive(Drop, Clone)]
struct PreparedContract {
struct ContractClass {
class_hash: ClassHash,
constructor_calldata: @Array::<felt252>,
}

#[derive(Drop, Clone)]
struct RevertedTransaction {
panic_data: Array::<felt252>,
}

trait ContractClassTrait {
fn precalculate_address(self: @ContractClass, constructor_calldata: @Array::<felt252>) -> ContractAddress;
fn deploy(self: @ContractClass, constructor_calldata: @Array::<felt252>) -> Result<ContractAddress, RevertedTransaction>;
}

trait RevertedTransactionTrait {
fn first(self: @RevertedTransaction) -> felt252;
}
Expand All @@ -34,54 +38,73 @@ impl RevertedTransactionImpl of RevertedTransactionTrait {
}
}

fn declare(contract: felt252) -> ClassHash {
let span = cheatcode::<'declare'>(array![contract].span());
impl ContractClassImpl of ContractClassTrait {
fn precalculate_address(self: @ContractClass, constructor_calldata: @Array::<felt252>) -> ContractAddress {
let mut inputs: Array::<felt252> = _prepare_calldata(self.class_hash, constructor_calldata);

let exit_code = *span[0];
let result = *span[1];
assert(exit_code == 0, 'declare should never fail');
result.try_into().unwrap()
}
let outputs = cheatcode::<'precalculate_address'>(inputs.span());
(*outputs[0]).try_into().unwrap()
}

fn deploy(prepared_contract: PreparedContract) -> Result::<ContractAddress, RevertedTransaction> {
let PreparedContract{class_hash, constructor_calldata } = prepared_contract;
let mut inputs = array![class_hash.into()];
fn deploy(self: @ContractClass, constructor_calldata: @Array::<felt252>) -> Result<ContractAddress, RevertedTransaction> {
piotmag769 marked this conversation as resolved.
Show resolved Hide resolved
let mut inputs = _prepare_calldata(self.class_hash, constructor_calldata);

let outputs = cheatcode::<'deploy'>(inputs.span());
let exit_code = *outputs[0];

if exit_code == 0 {
let result = *outputs[1];
Result::<ContractAddress, RevertedTransaction>::Ok(result.try_into().unwrap())
} else {
let panic_data_len_felt = *outputs[1];
let panic_data_len = panic_data_len_felt.try_into().unwrap();
let mut panic_data = array![];

let offset = 2;
let mut i = offset;
loop {
if panic_data_len + offset == i {
break ();
}
panic_data.append(*outputs[i]);
i += 1;
};

Result::<ContractAddress, RevertedTransaction>::Err(RevertedTransaction { panic_data })
}
}
}

fn _prepare_calldata(class_hash: @ClassHash, constructor_calldata: @Array::<felt252>) -> Array::<felt252> {
let class_hash: felt252 = class_hash.clone().into();
let mut inputs: Array::<felt252> = array![class_hash];
let calldata_len_felt = constructor_calldata.len().into();
inputs.append(calldata_len_felt);

let calldata_len = constructor_calldata.len();
let mut i = 0;

loop {
if calldata_len == i {
break ();
if i == calldata_len {
break();
}
inputs.append(*constructor_calldata[i]);
i += 1;
};

let outputs = cheatcode::<'deploy'>(inputs.span());
let exit_code = *outputs[0];

if exit_code == 0 {
let result = *outputs[1];
Result::<ContractAddress, RevertedTransaction>::Ok(result.try_into().unwrap())
} else {
let panic_data_len_felt = *outputs[1];
let panic_data_len = panic_data_len_felt.try_into().unwrap();
let mut panic_data = array![];

let offset = 2;
let mut i = offset;
loop {
if panic_data_len + offset == i {
break ();
}
panic_data.append(*outputs[i]);
i += 1;
};

Result::<ContractAddress, RevertedTransaction>::Err(RevertedTransaction { panic_data })
inputs
}

fn declare(contract: felt252) -> ContractClass {
let span = cheatcode::<'declare'>(array![contract].span());

let exit_code = *span[0];
let result = *span[1];
assert(exit_code == 0, 'declare should never fail');
let class_hash = result.try_into().unwrap();

ContractClass {
class_hash
}
}

Expand Down
4 changes: 2 additions & 2 deletions cheatcodes/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
mod cheatcodes;

use cheatcodes::declare;
use cheatcodes::deploy;

use cheatcodes::PreparedContract;
use cheatcodes::RevertedTransaction;
use cheatcodes::RevertedTransactionTrait;
use cheatcodes::ContractClass;
use cheatcodes::ContractClassTrait;

use cheatcodes::start_prank;
use cheatcodes::stop_prank;
Expand Down
1 change: 1 addition & 0 deletions crates/cheatable-starknet/src/cheatcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use thiserror::Error;
pub mod declare;
pub mod deploy;
pub mod prank;
pub mod precalculate_address;
pub mod roll;
pub mod warp;

Expand Down
15 changes: 7 additions & 8 deletions crates/cheatable-starknet/src/cheatcodes/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{
};
use anyhow::{Context, Result};
use blockifier::abi::abi_utils::selector_from_name;
use blockifier::execution::execution_utils::felt_to_stark_felt;
use num_traits::cast::ToPrimitive;

use blockifier::execution::entry_point::CallInfo;
Expand All @@ -28,7 +29,7 @@ use cairo_lang_runner::casm_run::MemBuffer;

impl CheatedState {
pub fn deploy(
&self,
&mut self,
buffer: &mut MemBuffer,
blockifier_state: &mut CachedState<DictStateReader>,
inputs: &[Felt252],
Expand All @@ -38,18 +39,16 @@ impl CheatedState {
let class_hash = inputs[0].clone();

let calldata_length = inputs[1].to_usize().unwrap();
let mut calldata = vec![];
for felt in inputs.iter().skip(2).take(calldata_length) {
calldata.push(felt.clone());
}

let calldata = Vec::from(&inputs[2..(2 + calldata_length)]);

// Deploy a contract using syscall deploy.
let account_address = ContractAddress(patricia_key!(TEST_ACCOUNT_CONTRACT_ADDRESS));
let block_context = build_block_context();
let entry_point_selector = selector_from_name("deploy_contract");
let salt = ContractAddressSalt::default();
let salt = self.get_salt();
let class_hash = ClassHash(StarkFelt::new(class_hash.to_be_bytes()).unwrap());

self.increment_deploy_salt_base();
let contract_class = blockifier_state.get_compiled_contract_class(&class_hash)?;
if contract_class.constructor_selector().is_none() && !calldata.is_empty() {
write_cheatcode_panic(
Expand Down Expand Up @@ -126,7 +125,7 @@ fn create_execute_calldata(
];
let mut calldata: Vec<StarkFelt> = calldata
.iter()
.map(|data| StarkFelt::new(data.to_be_bytes()).unwrap())
.map(|data| felt_to_stark_felt(data))
.collect();
execute_calldata.append(&mut calldata);
Calldata(execute_calldata.into())
Expand Down
52 changes: 52 additions & 0 deletions crates/cheatable-starknet/src/cheatcodes/precalculate_address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use crate::constants::TEST_ACCOUNT_CONTRACT_ADDRESS;

use crate::{cheatcodes::EnhancedHintError, CheatedState};
use blockifier::execution::execution_utils::felt_to_stark_felt;
use cairo_felt::Felt252;
use cairo_lang_runner::casm_run::MemBuffer;
use num_traits::cast::ToPrimitive;
use starknet_api::core::PatriciaKey;
use starknet_api::core::{calculate_contract_address, ClassHash, ContractAddress};
use starknet_api::hash::{StarkFelt, StarkHash};
use starknet_api::transaction::Calldata;

use starknet_api::patricia_key;

impl CheatedState {
pub fn precalculate_address(
&self,
buffer: &mut MemBuffer,
inputs: &[Felt252],
) -> Result<(), EnhancedHintError> {
let class_hash = inputs[0].clone();

let account_address = ContractAddress(patricia_key!(TEST_ACCOUNT_CONTRACT_ADDRESS));
let class_hash = ClassHash(StarkFelt::new(class_hash.to_be_bytes()).unwrap());
let salt = self.get_salt();
let calldata_length = inputs[1].to_usize().unwrap();

let calldata = Vec::from(&inputs[2..(2 + calldata_length)]);

let execute_calldata = create_execute_calldata(&calldata);
let contract_address =
calculate_contract_address(salt, class_hash, &execute_calldata, account_address)
.unwrap();

let contract_address = Felt252::from_bytes_be((*contract_address.0.key()).bytes());

buffer
.write(contract_address)
.expect("Failed to insert declared contract class hash");
Ok(())
}
}

fn create_execute_calldata(calldata: &[Felt252]) -> Calldata {
let mut execute_calldata = vec![];
let mut calldata: Vec<StarkFelt> = calldata
.iter()
.map(|data| felt_to_stark_felt(data))
.collect();
execute_calldata.append(&mut calldata);
Calldata(execute_calldata.into())
piotmag769 marked this conversation as resolved.
Show resolved Hide resolved
tkumor3 marked this conversation as resolved.
Show resolved Hide resolved
}
12 changes: 12 additions & 0 deletions crates/cheatable-starknet/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use cairo_felt::Felt252;
use starknet_api::core::ContractAddress;
use starknet_api::hash::StarkFelt;
use starknet_api::transaction::ContractAddressSalt;
use std::collections::HashMap;

pub mod cheatcodes;
Expand All @@ -11,6 +13,7 @@ pub struct CheatedState {
pub rolled_contracts: HashMap<ContractAddress, Felt252>,
pub pranked_contracts: HashMap<ContractAddress, ContractAddress>,
pub warped_contracts: HashMap<ContractAddress, Felt252>,
pub deploy_salt_base: u32,
}

impl CheatedState {
Expand All @@ -20,8 +23,17 @@ impl CheatedState {
rolled_contracts: HashMap::new(),
pranked_contracts: HashMap::new(),
warped_contracts: HashMap::new(),
deploy_salt_base: 0,
}
}

pub fn increment_deploy_salt_base(&mut self) -> () {
self.deploy_salt_base += 1;
}

pub fn get_salt(&self) -> ContractAddressSalt {
ContractAddressSalt(StarkFelt::from(self.deploy_salt_base))
}
}

impl Default for CheatedState {
Expand Down
3 changes: 3 additions & 0 deletions crates/forge/src/cheatcodes_hint_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ impl CairoHintProcessor<'_> {
print(inputs);
Ok(())
}
"precalculate_address" => self
.cheated_state
.precalculate_address(&mut buffer, &inputs),
piotmag769 marked this conversation as resolved.
Show resolved Hide resolved
_ => Err(anyhow!("Unknown cheatcode selector: {selector}")).map_err(Into::into),
}?;

Expand Down
25 changes: 25 additions & 0 deletions crates/forge/tests/data/contracts/constructor_simple.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#[starknet::contract]
mod ConstructorSimple {
#[storage]
struct Storage {
number: felt252
}

#[constructor]
fn constructor(ref self: ContractState, number: felt252) {
self.number.write(number);
}

#[external(v0)]
fn add_to_number(ref self: ContractState, number: felt252) -> felt252 {
let new_number = self.number.read() + number;
self.number.write(new_number);
new_number
}

#[external(v0)]
fn get_number(self: @ContractState) -> felt252 {
self.number.read()
}
}

tkumor3 marked this conversation as resolved.
Show resolved Hide resolved
8 changes: 4 additions & 4 deletions crates/forge/tests/data/simple_package/tests/contract.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ use traits::TryInto;
use starknet::ContractAddress;
use starknet::Felt252TryIntoContractAddress;

use cheatcodes::{ declare, PreparedContract, deploy };
use cheatcodes::{ declare, ContractClassTrait };

use simple_package::hello_starknet::IHelloStarknetDispatcher;
use simple_package::hello_starknet::IHelloStarknetDispatcherTrait;

#[test]
fn call_and_invoke() {
let class_hash = declare('HelloStarknet');
let prepared = PreparedContract { class_hash: class_hash, constructor_calldata: @ArrayTrait::new() };
let contract_address = deploy(prepared).unwrap();
let contract = declare('HelloStarknet');
let constructor_calldata = @ArrayTrait::new();
let contract_address = contract.deploy(constructor_calldata).unwrap();
let dispatcher = IHelloStarknetDispatcher { contract_address };

let balance = dispatcher.get_balance();
Expand Down
Loading