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

Commit 152a3e4

Browse files
fmolettajuanbono
andauthored
Execute Declare transactions using the RpcState + Various fixes related to Declare txs (#1094)
* Add test case * Fix get_onchain_data_segment_length * Debug setup * Fix get_onchain_data_segment_length * Add StorageChangesCount struct * Update test values * Update test values * fmt * Use StorageChangesCount struct in state method * Fix implicated code * Update doc * Update test values * Rename method for consistency * Add note comment * Remove hardcoded contract address * Remove txt files * Remove dbg prints * Remove dbg prints * Format * Restore blockifier version * Restore tests * Restore newlines * Restore newlines * Remove txt file * fmt * Add test case with declare * Deserialize Declare transactions * Create blockifier Declare transaction * Fix/Refactor `State::count actual storage changes` (#1086) * Add StorageChangesCount struct * Use StorageChangesCount struct in state method * Fix implicated code * Update doc * Update test values * Rename method for consistency * Add the ability to execute `DeployAccount` transactions using the `RpcState` (#1089) * Add test case * Fix get_onchain_data_segment_length * Debug setup * Add StorageChangesCount struct * Use StorageChangesCount struct in state method * Fix implicated code * Update doc * Update test values * Rename method for consistency * Add note comment * Remove hardcoded contract address * Remove txt files * Remove dbg prints * Remove dbg prints * Format * Restore blockifier version * Restore tests * Restore newlines * Restore newlines * Remove txt file * fmt * Fix bug in `From<StarknetRsContractClass> for CompiledClass` implementation (#1090) * Fix bug in CompiledClass * Add tests * fetch class hash from the next block in declare tx * Return an error if a class_hash is not declared + add tests for declare tx * Fix error msg * Add support for DeclareV0-1 in sir_tests * Make Sierra class optional in declare v2 + other changes * Add support for DeclareV2 * Uncomment test * fix * Use new_with_sierra_class_hash_and_tx_hash * use CompiledClassHash instead of CompiledClass where applicatble * Handle nonce in declare v2 + run fmt * Set casm class before counting state changes in declare v2 * Changes * Make sierra class hash non-optional * fix + clippy * Use state_reader instead of creating a state to fetch the next block s contract classes * Add removed test * Update test values --------- Co-authored-by: Juan Bono <juanbono94@gmail.com>
1 parent 5edd850 commit 152a3e4

File tree

13 files changed

+283
-62
lines changed

13 files changed

+283
-62
lines changed

rpc_state_reader/src/rpc_state.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,12 +401,12 @@ impl RpcState {
401401
}
402402
}
403403

404-
pub fn get_contract_class(&self, class_hash: &ClassHash) -> SNContractClass {
404+
pub fn get_contract_class(&self, class_hash: &ClassHash) -> Option<SNContractClass> {
405405
self.rpc_call_result(
406406
"starknet_getClass",
407407
&json!([self.block.to_value().unwrap(), class_hash.0.to_string()]),
408408
)
409-
.unwrap()
409+
.ok()
410410
}
411411

412412
pub fn get_class_hash_at(&self, contract_address: &ContractAddress) -> ClassHash {

rpc_state_reader/src/utils.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use starknet_api::{
1111
core::EntryPointSelector,
1212
deprecated_contract_class::{EntryPoint, EntryPointOffset, EntryPointType},
1313
hash::{StarkFelt, StarkHash},
14-
transaction::{InvokeTransaction, Transaction},
14+
transaction::{DeclareTransaction, InvokeTransaction, Transaction},
1515
};
1616

1717
#[derive(Debug, Deserialize)]
@@ -85,6 +85,20 @@ pub fn deserialize_transaction_json(
8585
"DEPLOY_ACCOUNT" => Ok(Transaction::DeployAccount(serde_json::from_value(
8686
transaction,
8787
)?)),
88+
"DECLARE" => match tx_version.as_str() {
89+
"0x0" => Ok(Transaction::Declare(DeclareTransaction::V0(
90+
serde_json::from_value(transaction)?,
91+
))),
92+
"0x1" => Ok(Transaction::Declare(DeclareTransaction::V1(
93+
serde_json::from_value(transaction)?,
94+
))),
95+
"0x2" => Ok(Transaction::Declare(DeclareTransaction::V2(
96+
serde_json::from_value(transaction)?,
97+
))),
98+
x => Err(serde::de::Error::custom(format!(
99+
"unimplemented declare version: {x}"
100+
))),
101+
},
88102
x => Err(serde::de::Error::custom(format!(
89103
"unimplemented transaction type deserialization: {x}"
90104
))),

rpc_state_reader/tests/blockifier_tests.rs

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ use blockifier::{
33
execution::{contract_class::ContractClass, entry_point::CallInfo},
44
state::{
55
cached_state::{CachedState, GlobalContractCache},
6+
errors::StateError,
67
state_api::{StateReader, StateResult},
78
},
89
transaction::{
910
account_transaction::AccountTransaction,
1011
objects::TransactionExecutionInfo,
11-
transactions::{DeployAccountTransaction, ExecutableTransaction},
12+
transactions::{DeclareTransaction, DeployAccountTransaction, ExecutableTransaction},
1213
},
1314
};
1415
use blockifier::{
@@ -66,7 +67,7 @@ impl StateReader for RpcStateReader {
6667
class_hash: &ClassHash,
6768
) -> StateResult<ContractClass> {
6869
Ok(match self.0.get_contract_class(class_hash) {
69-
SNContractClass::Legacy(compressed_legacy_cc) => {
70+
Some(SNContractClass::Legacy(compressed_legacy_cc)) => {
7071
let as_str = utils::decode_reader(compressed_legacy_cc.program).unwrap();
7172
let program = Program::from_bytes(as_str.as_bytes(), None).unwrap();
7273
let entry_points_by_type = utils::map_entry_points_by_type_legacy(
@@ -78,7 +79,7 @@ impl StateReader for RpcStateReader {
7879
});
7980
BlockifierContractClass::V0(ContractClassV0(inner))
8081
}
81-
SNContractClass::Sierra(flattened_sierra_cc) => {
82+
Some(SNContractClass::Sierra(flattened_sierra_cc)) => {
8283
let middle_sierra: utils::MiddleSierraContractClass = {
8384
let v = serde_json::to_value(flattened_sierra_cc).unwrap();
8485
serde_json::from_value(v).unwrap()
@@ -93,6 +94,7 @@ impl StateReader for RpcStateReader {
9394
let casm_cc = CasmContractClass::from_contract_class(sierra_cc, false).unwrap();
9495
BlockifierContractClass::V1(casm_cc.try_into().unwrap())
9596
}
97+
None => return Err(StateError::UndeclaredClassHash(*class_hash)),
9698
})
9799
}
98100

@@ -194,6 +196,17 @@ pub fn execute_tx(
194196
contract_address,
195197
})
196198
}
199+
SNTransaction::Declare(tx) => {
200+
// Fetch the contract_class from the next block (as we don't have it in the previous one)
201+
let mut next_block_state_reader =
202+
RpcStateReader(RpcState::new_infura(network, (block_number.next()).into()));
203+
let contract_class = next_block_state_reader
204+
.get_compiled_contract_class(&tx.class_hash())
205+
.unwrap();
206+
207+
let declare = DeclareTransaction::new(tx, tx_hash, contract_class).unwrap();
208+
AccountTransaction::Declare(declare)
209+
}
197210
_ => unimplemented!(),
198211
};
199212

@@ -405,3 +418,38 @@ fn blockifier_test_case_reverted_tx(hash: &str, block_number: u64, chain: RpcCha
405418
);
406419
}
407420
}
421+
422+
#[test_case(
423+
// Declare tx
424+
"0x60506c49e65d84e2cdd0e9142dc43832a0a59cb6a9cbcce1ab4f57c20ba4afb",
425+
347899, // real block 347900
426+
RpcChain::MainNet
427+
)]
428+
#[test_case(
429+
// Declare tx
430+
"0x1088aa18785779e1e8eef406dc495654ad42a9729b57969ad0dbf2189c40bee",
431+
271887, // real block 271888
432+
RpcChain::MainNet
433+
)]
434+
fn blockifier_test_case_declare_tx(hash: &str, block_number: u64, chain: RpcChain) {
435+
let (tx_info, _trace, receipt) = execute_tx(hash, chain, BlockNumber(block_number));
436+
let TransactionExecutionInfo {
437+
execute_call_info,
438+
actual_fee,
439+
..
440+
} = tx_info;
441+
442+
assert!(execute_call_info.is_none());
443+
444+
let actual_fee = actual_fee.0;
445+
if receipt.actual_fee != actual_fee {
446+
let diff = 100 * receipt.actual_fee.abs_diff(actual_fee) / receipt.actual_fee;
447+
448+
if diff >= 5 {
449+
assert_eq!(
450+
actual_fee, receipt.actual_fee,
451+
"actual_fee mismatch differs from the baseline by more than 5% ({diff}%)",
452+
);
453+
}
454+
}
455+
}

rpc_state_reader/tests/sir_tests.rs

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ use starknet_api::{
88
hash::{StarkFelt, StarkHash},
99
stark_felt,
1010
state::StorageKey,
11-
transaction::{Transaction as SNTransaction, TransactionHash},
11+
transaction::{Transaction as SNTransaction, TransactionHash, TransactionVersion},
1212
};
1313
use starknet_in_rust::{
14-
core::errors::state_errors::StateError,
14+
core::{contract_address::compute_casm_class_hash, errors::state_errors::StateError},
1515
definitions::{
1616
block_context::{BlockContext, StarknetChainId, StarknetOsConfig},
1717
constants::{
@@ -28,7 +28,7 @@ use starknet_in_rust::{
2828
state_cache::StorageEntry,
2929
BlockInfo,
3030
},
31-
transaction::{DeployAccount, InvokeFunction},
31+
transaction::{Declare, DeclareV2, DeployAccount, InvokeFunction},
3232
utils::{Address, ClassHash},
3333
};
3434

@@ -42,7 +42,9 @@ pub struct RpcStateReader(RpcState);
4242
impl StateReader for RpcStateReader {
4343
fn get_contract_class(&self, class_hash: &ClassHash) -> Result<CompiledClass, StateError> {
4444
let hash = SNClassHash(StarkHash::new(*class_hash).unwrap());
45-
Ok(CompiledClass::from(self.0.get_contract_class(&hash)))
45+
Ok(CompiledClass::from(
46+
self.0.get_contract_class(&hash).unwrap(),
47+
))
4648
}
4749

4850
fn get_class_hash_at(&self, contract_address: &Address) -> Result<ClassHash, StateError> {
@@ -148,6 +150,64 @@ pub fn execute_tx_configurable(
148150
.unwrap()
149151
.create_for_simulation(skip_validate, false, false, false)
150152
}
153+
SNTransaction::Declare(tx) => {
154+
// Fetch the contract_class from the next block (as we don't have it in the previous one)
155+
let next_block_state_reader =
156+
RpcStateReader(RpcState::new_infura(network, (block_number.next()).into()));
157+
let contract_class = next_block_state_reader
158+
.get_contract_class(tx.class_hash().0.bytes().try_into().unwrap())
159+
.unwrap();
160+
161+
if tx.version() != TransactionVersion(2_u8.into()) {
162+
let contract_class = match contract_class {
163+
CompiledClass::Deprecated(cc) => cc.as_ref().clone(),
164+
_ => unreachable!(),
165+
};
166+
167+
let declare = Declare::new_with_tx_and_class_hash(
168+
contract_class,
169+
Address(Felt252::from_bytes_be(tx.sender_address().0.key().bytes())),
170+
tx.max_fee().0,
171+
Felt252::from_bytes_be(tx.version().0.bytes()),
172+
tx.signature()
173+
.0
174+
.iter()
175+
.map(|f| Felt252::from_bytes_be(f.bytes()))
176+
.collect(),
177+
Felt252::from_bytes_be(tx.nonce().0.bytes()),
178+
Felt252::from_bytes_be(tx_hash.0.bytes()),
179+
tx.class_hash().0.bytes().try_into().unwrap(),
180+
)
181+
.unwrap();
182+
declare.create_for_simulation(skip_validate, false, false, false)
183+
} else {
184+
let contract_class = match contract_class {
185+
CompiledClass::Casm(cc) => cc.as_ref().clone(),
186+
_ => unreachable!(),
187+
};
188+
189+
let compiled_class_hash = compute_casm_class_hash(&contract_class).unwrap();
190+
191+
let declare = DeclareV2::new_with_sierra_class_hash_and_tx_hash(
192+
None,
193+
Felt252::from_bytes_be(tx.class_hash().0.bytes()),
194+
Some(contract_class),
195+
compiled_class_hash,
196+
Address(Felt252::from_bytes_be(tx.sender_address().0.key().bytes())),
197+
tx.max_fee().0,
198+
Felt252::from_bytes_be(tx.version().0.bytes()),
199+
tx.signature()
200+
.0
201+
.iter()
202+
.map(|f| Felt252::from_bytes_be(f.bytes()))
203+
.collect(),
204+
Felt252::from_bytes_be(tx.nonce().0.bytes()),
205+
Felt252::from_bytes_be(tx_hash.0.bytes()),
206+
)
207+
.unwrap();
208+
declare.create_for_simulation(skip_validate, false, false, false)
209+
}
210+
}
151211
_ => unimplemented!(),
152212
};
153213

@@ -441,3 +501,38 @@ fn test_validate_fee(hash: &str, block_number: u64, chain: RpcChain) {
441501
assert_eq!(tx_info.actual_fee, receipt.actual_fee);
442502
assert!(tx_info_without_fee.actual_fee < tx_info.actual_fee);
443503
}
504+
505+
#[test_case(
506+
// Declare tx
507+
"0x60506c49e65d84e2cdd0e9142dc43832a0a59cb6a9cbcce1ab4f57c20ba4afb",
508+
347899, // real block 347900
509+
RpcChain::MainNet
510+
)]
511+
#[test_case(
512+
// Declare tx
513+
"0x1088aa18785779e1e8eef406dc495654ad42a9729b57969ad0dbf2189c40bee",
514+
271887, // real block 271888
515+
RpcChain::MainNet
516+
)]
517+
fn starknet_in_rust_test_case_declare_tx(hash: &str, block_number: u64, chain: RpcChain) {
518+
let (tx_info, _trace, receipt) = execute_tx(hash, chain, BlockNumber(block_number));
519+
let TransactionExecutionInfo {
520+
call_info,
521+
actual_fee,
522+
..
523+
} = tx_info;
524+
525+
assert!(call_info.is_none());
526+
527+
let actual_fee = actual_fee;
528+
if receipt.actual_fee != actual_fee {
529+
let diff = 100 * receipt.actual_fee.abs_diff(actual_fee) / receipt.actual_fee;
530+
531+
if diff >= 5 {
532+
assert_eq!(
533+
actual_fee, receipt.actual_fee,
534+
"actual_fee mismatch differs from the baseline by more than 5% ({diff}%)",
535+
);
536+
}
537+
}
538+
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,7 @@ mod test {
870870
nonce: 0.into(),
871871
hash_value: 0.into(),
872872
compiled_class_hash: TEST_FIB_COMPILED_CONTRACT_CLASS_HASH.clone(),
873-
sierra_contract_class,
873+
sierra_contract_class: Some(sierra_contract_class),
874874
sierra_class_hash,
875875
casm_class: Default::default(),
876876
skip_execute: false,

src/state/cached_state.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ impl<T: StateReader> State for CachedState<T> {
266266
let compiled_class_hash = compiled_class_hash.to_be_bytes();
267267

268268
self.cache
269-
.class_hash_to_compiled_class_hash
269+
.compiled_class_hash_writes
270270
.insert(class_hash, compiled_class_hash);
271271
Ok(())
272272
}

src/state/mod.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ pub mod state_cache;
66

77
use crate::{
88
core::errors::state_errors::StateError,
9-
services::api::contract_classes::compiled_class::CompiledClass,
10-
utils::{get_keys, to_cache_state_storage_mapping, to_state_diff_storage_mapping},
9+
utils::{
10+
get_keys, to_cache_state_storage_mapping, to_state_diff_storage_mapping, CompiledClassHash,
11+
},
1112
};
1213
use cairo_vm::{felt::Felt252, vm::runners::cairo_runner::ExecutionResources};
1314
use getset::Getters;
@@ -106,15 +107,15 @@ impl ExecutionResourcesManager {
106107
pub struct StateDiff {
107108
pub(crate) address_to_class_hash: HashMap<Address, ClassHash>,
108109
pub(crate) address_to_nonce: HashMap<Address, Felt252>,
109-
pub(crate) class_hash_to_compiled_class: HashMap<ClassHash, CompiledClass>,
110+
pub(crate) class_hash_to_compiled_class: HashMap<ClassHash, CompiledClassHash>,
110111
pub(crate) storage_updates: HashMap<Address, HashMap<Felt252, Felt252>>,
111112
}
112113

113114
impl StateDiff {
114115
pub const fn new(
115116
address_to_class_hash: HashMap<Address, ClassHash>,
116117
address_to_nonce: HashMap<Address, Felt252>,
117-
class_hash_to_compiled_class: HashMap<ClassHash, CompiledClass>,
118+
class_hash_to_compiled_class: HashMap<ClassHash, CompiledClassHash>,
118119
storage_updates: HashMap<Address, HashMap<Felt252, Felt252>>,
119120
) -> Self {
120121
StateDiff {

0 commit comments

Comments
 (0)