-
Notifications
You must be signed in to change notification settings - Fork 66
Expand file tree
/
Copy pathoffline_state_reader.rs
More file actions
306 lines (275 loc) · 12 KB
/
offline_state_reader.rs
File metadata and controls
306 lines (275 loc) · 12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
use std::fs;
use blockifier::abi::constants;
use blockifier::blockifier::config::TransactionExecutorConfig;
use blockifier::blockifier::transaction_executor::TransactionExecutor;
use blockifier::blockifier_versioned_constants::VersionedConstants;
use blockifier::bouncer::BouncerConfig;
use blockifier::context::BlockContext;
use blockifier::execution::contract_class::RunnableCompiledClass;
use blockifier::state::cached_state::{CommitmentStateDiff, StateMaps};
use blockifier::state::contract_class_manager::ContractClassManager;
use blockifier::state::errors::StateError;
use blockifier::state::global_cache::CompiledClasses;
use blockifier::state::state_api::{StateReader, StateResult};
use blockifier::state::state_reader_and_contract_manager::{
FetchCompiledClasses,
StateReaderAndContractManager,
};
use blockifier::state::utils::get_compiled_class_hash_v2 as default_get_compiled_class_hash_v2;
use blockifier::transaction::transaction_execution::Transaction as BlockifierTransaction;
use serde::{Deserialize, Serialize};
use starknet_api::block::{BlockHash, BlockHashAndNumber, BlockInfo, BlockNumber, StarknetVersion};
use starknet_api::core::{ChainId, ClassHash, CompiledClassHash, ContractAddress, Nonce};
use starknet_api::state::{SierraContractClass, StorageKey};
use starknet_api::transaction::{Transaction, TransactionHash};
use starknet_api::versioned_constants_logic::VersionedConstantsTrait;
use starknet_core::types::ContractClass as StarknetContractClass;
use starknet_types_core::felt::Felt;
use crate::compile::{legacy_to_contract_class_v0, sierra_to_versioned_contract_class_v1};
use crate::errors::ReexecutionResult;
use crate::state_reader::reexecution_state_reader::{
ConsecutiveReexecutionStateReaders,
ReexecutionStateReader,
};
use crate::state_reader::rpc_state_reader::StarknetContractClassMapping;
use crate::utils::get_chain_info;
pub struct OfflineReexecutionData {
offline_state_reader_prev_block: OfflineStateReader,
block_context_next_block: BlockContext,
transactions_next_block: Vec<BlockifierTransaction>,
state_diff_next_block: CommitmentStateDiff,
}
#[derive(Serialize, Deserialize)]
pub struct SerializableDataNextBlock {
pub block_info_next_block: BlockInfo,
pub starknet_version: StarknetVersion,
pub transactions_next_block: Vec<(Transaction, TransactionHash)>,
pub state_diff_next_block: CommitmentStateDiff,
pub declared_classes: StarknetContractClassMapping,
}
#[derive(Serialize, Deserialize)]
pub struct SerializableDataPrevBlock {
pub state_maps: StateMaps,
pub contract_class_mapping: StarknetContractClassMapping,
}
#[derive(Serialize, Deserialize)]
pub struct SerializableOfflineReexecutionData {
pub serializable_data_prev_block: SerializableDataPrevBlock,
pub serializable_data_next_block: SerializableDataNextBlock,
pub chain_id: ChainId,
pub old_block_hash: BlockHash,
}
impl SerializableOfflineReexecutionData {
pub fn write_to_file(&self, full_file_path: &str) -> ReexecutionResult<()> {
let file_path = full_file_path.rsplit_once('/').expect("Invalid file path.").0;
fs::create_dir_all(file_path)
.unwrap_or_else(|err| panic!("Failed to create directory {file_path}. Error: {err}"));
fs::write(full_file_path, serde_json::to_string_pretty(&self)?)
.unwrap_or_else(|err| panic!("Failed to write to file {full_file_path}. Error: {err}"));
Ok(())
}
pub fn read_from_file(full_file_path: &str) -> ReexecutionResult<Self> {
let file_content = fs::read_to_string(full_file_path).unwrap_or_else(|err| {
panic!("Failed to read reexecution data from file {full_file_path}. Error: {err}")
});
Ok(serde_json::from_str(&file_content)?)
}
}
impl From<SerializableOfflineReexecutionData> for OfflineReexecutionData {
fn from(value: SerializableOfflineReexecutionData) -> Self {
let SerializableOfflineReexecutionData {
serializable_data_prev_block:
SerializableDataPrevBlock { state_maps, contract_class_mapping },
serializable_data_next_block:
SerializableDataNextBlock {
block_info_next_block,
starknet_version,
transactions_next_block,
state_diff_next_block,
declared_classes,
},
chain_id,
old_block_hash,
} = value;
let offline_state_reader_prev_block =
OfflineStateReader { state_maps, contract_class_mapping, old_block_hash };
// Use the declared classes from the next block to allow retrieving the class info.
let transactions_next_block =
OfflineStateReader { contract_class_mapping: declared_classes, ..Default::default() }
.api_txs_to_blockifier_txs_next_block(transactions_next_block)
.expect("Failed to convert starknet-api transactions to blockifier transactions.");
// Disable casm hash migration for reexecution.
let mut versioned_constants = VersionedConstants::get(&starknet_version).unwrap().clone();
versioned_constants.enable_casm_hash_migration = false;
Self {
offline_state_reader_prev_block,
block_context_next_block: BlockContext::new(
block_info_next_block,
get_chain_info(&chain_id, None),
versioned_constants,
BouncerConfig::max(),
),
transactions_next_block,
state_diff_next_block,
}
}
}
#[derive(Clone, Default)]
pub struct OfflineStateReader {
pub state_maps: StateMaps,
pub contract_class_mapping: StarknetContractClassMapping,
pub old_block_hash: BlockHash,
}
impl StateReader for OfflineStateReader {
fn get_storage_at(
&self,
contract_address: ContractAddress,
key: StorageKey,
) -> StateResult<Felt> {
Ok(*self.state_maps.storage.get(&(contract_address, key)).ok_or(
StateError::StateReadError(format!(
"Missing Storage Value at contract_address: {contract_address}, key:{key:?}"
)),
)?)
}
fn get_nonce_at(&self, contract_address: ContractAddress) -> StateResult<Nonce> {
Ok(*self.state_maps.nonces.get(&contract_address).ok_or(StateError::StateReadError(
format!("Missing nonce at contract_address: {contract_address}"),
))?)
}
fn get_class_hash_at(&self, contract_address: ContractAddress) -> StateResult<ClassHash> {
Ok(*self.state_maps.class_hashes.get(&contract_address).ok_or(
StateError::StateReadError(format!(
"Missing class hash at contract_address: {contract_address}"
)),
)?)
}
fn get_compiled_class(&self, class_hash: ClassHash) -> StateResult<RunnableCompiledClass> {
match self.get_contract_class(&class_hash)? {
StarknetContractClass::Sierra(sierra) => {
let sierra_contract = SierraContractClass::from(sierra);
let (casm, _) = sierra_to_versioned_contract_class_v1(sierra_contract).unwrap();
Ok(casm.try_into().unwrap())
}
StarknetContractClass::Legacy(legacy) => {
Ok(legacy_to_contract_class_v0(legacy).unwrap().try_into().unwrap())
}
}
}
fn get_compiled_class_hash(&self, _class_hash: ClassHash) -> StateResult<CompiledClassHash> {
unimplemented!("The offline state reader does not support get_compiled_class_hash.")
}
fn get_compiled_class_hash_v2(
&self,
class_hash: ClassHash,
compiled_class: &RunnableCompiledClass,
) -> StateResult<CompiledClassHash> {
default_get_compiled_class_hash_v2(self, class_hash, compiled_class)
}
}
impl ReexecutionStateReader for OfflineStateReader {
fn get_contract_class(&self, class_hash: &ClassHash) -> StateResult<StarknetContractClass> {
Ok(self
.contract_class_mapping
.get(class_hash)
.ok_or(StateError::UndeclaredClassHash(*class_hash))?
.clone())
}
fn get_old_block_hash(&self, _old_block_number: BlockNumber) -> ReexecutionResult<BlockHash> {
Ok(self.old_block_hash)
}
}
impl FetchCompiledClasses for OfflineStateReader {
fn get_compiled_classes(&self, class_hash: ClassHash) -> StateResult<CompiledClasses> {
let contract_class = self.get_contract_class(&class_hash)?;
self.starknet_core_contract_class_to_compiled_class(contract_class)
}
/// This check is no needed in the reexecution context.
/// We assume that all the classes returned successfuly by the OfflineStateReader are declared.
fn is_declared(&self, _class_hash: ClassHash) -> StateResult<bool> {
Ok(true)
}
}
impl OfflineStateReader {
pub fn get_transaction_executor(
self,
block_context_next_block: BlockContext,
transaction_executor_config: Option<TransactionExecutorConfig>,
contract_class_manager: &ContractClassManager,
) -> ReexecutionResult<TransactionExecutor<StateReaderAndContractManager<OfflineStateReader>>>
{
let old_block_number = BlockNumber(
block_context_next_block.block_info().block_number.0
- constants::STORED_BLOCK_HASH_BUFFER,
);
let hash = self.old_block_hash;
// We don't collect class cache metrics for the reexecution.
let class_cache_metrics = None;
let state_reader_and_contract_manager = StateReaderAndContractManager::new(
self,
contract_class_manager.clone(),
class_cache_metrics,
);
Ok(TransactionExecutor::<StateReaderAndContractManager<OfflineStateReader>>::pre_process_and_create(
state_reader_and_contract_manager,
block_context_next_block,
Some(BlockHashAndNumber { number: old_block_number, hash }),
transaction_executor_config.unwrap_or_default(),
)?)
}
}
pub struct OfflineConsecutiveStateReaders {
pub offline_state_reader_prev_block: OfflineStateReader,
pub block_context_next_block: BlockContext,
pub transactions_next_block: Vec<BlockifierTransaction>,
pub state_diff_next_block: CommitmentStateDiff,
contract_class_manager: ContractClassManager,
}
impl OfflineConsecutiveStateReaders {
pub fn new_from_file(
full_file_path: &str,
contract_class_manager: ContractClassManager,
) -> ReexecutionResult<Self> {
let serializable_offline_reexecution_data =
SerializableOfflineReexecutionData::read_from_file(full_file_path)?;
Ok(Self::new(serializable_offline_reexecution_data.into(), contract_class_manager))
}
pub fn new(
OfflineReexecutionData {
offline_state_reader_prev_block,
block_context_next_block,
transactions_next_block,
state_diff_next_block,
}: OfflineReexecutionData,
contract_class_manager: ContractClassManager,
) -> Self {
Self {
offline_state_reader_prev_block,
block_context_next_block,
transactions_next_block,
state_diff_next_block,
contract_class_manager,
}
}
}
impl ConsecutiveReexecutionStateReaders<StateReaderAndContractManager<OfflineStateReader>>
for OfflineConsecutiveStateReaders
{
fn pre_process_and_create_executor(
self,
transaction_executor_config: Option<TransactionExecutorConfig>,
) -> ReexecutionResult<TransactionExecutor<StateReaderAndContractManager<OfflineStateReader>>>
{
self.offline_state_reader_prev_block.get_transaction_executor(
self.block_context_next_block,
transaction_executor_config,
&self.contract_class_manager,
)
}
fn get_next_block_txs(&self) -> ReexecutionResult<Vec<BlockifierTransaction>> {
Ok(self.transactions_next_block.clone())
}
fn get_next_block_state_diff(&self) -> ReexecutionResult<CommitmentStateDiff> {
Ok(self.state_diff_next_block.clone())
}
}