fuel_vm/interpreter/diff/
storage.rs

1use core::fmt::Debug;
2use hashbrown::HashMap;
3
4use fuel_storage::{
5    StorageRead,
6    StorageSize,
7    StorageWrite,
8};
9use fuel_tx::ConsensusParameters;
10use fuel_types::{
11    BlobId,
12    BlockHeight,
13    Bytes32,
14    ContractId,
15};
16
17use crate::storage::{
18    BlobBytes,
19    BlobData,
20    ContractsAssetKey,
21    ContractsAssetsStorage,
22    ContractsStateData,
23    ContractsStateKey,
24    InterpreterStorage,
25    UploadedBytecode,
26    UploadedBytecodes,
27};
28
29use super::{
30    ExecutableTransaction,
31    Interpreter,
32    *,
33};
34
35#[derive(Debug)]
36/// The set of state changes that are recorded.
37pub(super) enum StorageDelta {
38    State(MappableDelta<ContractsStateKey, ContractsStateData>),
39    Assets(MappableDelta<ContractsAssetKey, u64>),
40    RawCode(MappableDelta<ContractId, Contract>),
41    UploadedBytecode(MappableDelta<Bytes32, UploadedBytecode>),
42    BlobData(MappableDelta<BlobId, BlobBytes>),
43}
44
45/// The set of states that are recorded.
46#[derive(Debug, Clone)]
47pub(super) enum StorageState {
48    State(MappableState<ContractsStateKey, ContractsStateData>),
49    Assets(MappableState<ContractsAssetKey, u64>),
50    RawCode(MappableState<ContractId, Contract>),
51    UploadedBytecode(MappableState<Bytes32, UploadedBytecode>),
52    BlobData(MappableState<BlobId, BlobBytes>),
53}
54
55#[derive(Debug)]
56/// A [`Mappable`] type that has changed.
57pub(super) enum MappableDelta<Key, Value> {
58    Replace(Key, Value, Option<Value>),
59    Take(Key, Value),
60}
61
62/// The state of a [`Mappable`] type.
63#[derive(Debug, Clone)]
64pub(super) struct MappableState<Key, Value> {
65    pub key: Key,
66    pub value: Option<Value>,
67}
68
69/// Records state changes of any [`Mappable`] type.
70pub(super) trait StorageType: Mappable {
71    /// Records a replace state change.
72    fn record_replace(
73        key: &Self::Key,
74        value: &Self::Value,
75        existing: Option<Self::OwnedValue>,
76    ) -> StorageDelta;
77
78    /// Records a take state change.
79    fn record_take(key: &Self::Key, value: Self::OwnedValue) -> StorageDelta;
80}
81
82#[derive(Debug)]
83pub struct Record<S>(pub(super) S, pub(super) Vec<StorageDelta>)
84where
85    S: InterpreterStorage;
86
87impl<M, S, Tx, Ecal> Interpreter<M, Record<S>, Tx, Ecal>
88where
89    S: InterpreterStorage,
90    Tx: ExecutableTransaction,
91{
92    /// Remove the [`Recording`] wrapper from the storage.
93    /// Recording storage changes has an overhead so it's
94    /// useful to be able to remove it once the diff is generated.
95    pub fn remove_recording(self) -> Interpreter<M, S, Tx, Ecal> {
96        Interpreter {
97            registers: self.registers,
98            memory: self.memory,
99            frames: self.frames,
100            receipts: self.receipts,
101            tx: self.tx,
102            initial_balances: self.initial_balances,
103            input_contracts: self.input_contracts,
104            input_contracts_index_to_output_index: self
105                .input_contracts_index_to_output_index,
106            storage: self.storage.0,
107            debugger: self.debugger,
108            context: self.context,
109            balances: self.balances,
110            panic_context: self.panic_context,
111            profiler: self.profiler,
112            interpreter_params: self.interpreter_params,
113            ecal_state: self.ecal_state,
114        }
115    }
116
117    /// Get the diff of changes to this VMs storage.
118    pub fn storage_diff(&self) -> Diff<Deltas> {
119        let mut diff = Diff {
120            changes: Vec::new(),
121        };
122        let mut contracts_state = Delta {
123            from: HashMap::new(),
124            to: HashMap::new(),
125        };
126        let mut contracts_assets = Delta {
127            from: HashMap::new(),
128            to: HashMap::new(),
129        };
130        let mut contracts_raw_code = Delta {
131            from: HashMap::new(),
132            to: HashMap::new(),
133        };
134        let mut uploaded_bytecode: Delta<HashMap<Bytes32, &UploadedBytecode>> = Delta {
135            from: HashMap::new(),
136            to: HashMap::new(),
137        };
138        let mut blob_data = Delta {
139            from: HashMap::new(),
140            to: HashMap::new(),
141        };
142
143        for delta in self.storage.1.iter() {
144            match delta {
145                StorageDelta::State(delta) => {
146                    mappable_delta_to_hashmap(&mut contracts_state, delta)
147                }
148                StorageDelta::Assets(delta) => {
149                    mappable_delta_to_hashmap(&mut contracts_assets, delta)
150                }
151                StorageDelta::RawCode(delta) => {
152                    mappable_delta_to_hashmap(&mut contracts_raw_code, delta)
153                }
154                StorageDelta::UploadedBytecode(delta) => {
155                    mappable_delta_to_hashmap(&mut uploaded_bytecode, delta)
156                }
157                StorageDelta::BlobData(delta) => {
158                    mappable_delta_to_hashmap(&mut blob_data, delta)
159                }
160            }
161        }
162        storage_state_to_changes(&mut diff, contracts_state, StorageState::State);
163        storage_state_to_changes(&mut diff, contracts_assets, StorageState::Assets);
164        storage_state_to_changes(&mut diff, contracts_raw_code, StorageState::RawCode);
165        storage_state_to_changes(
166            &mut diff,
167            uploaded_bytecode,
168            StorageState::UploadedBytecode,
169        );
170        storage_state_to_changes(&mut diff, blob_data, StorageState::BlobData);
171        diff
172    }
173}
174
175impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal>
176where
177    M: Memory,
178    S: InterpreterStorage,
179    Tx: ExecutableTransaction,
180{
181    /// Add a [`Recording`] wrapper around the storage to
182    /// record any changes this VM makes to it's storage.
183    /// Recording storage changes has an overhead so should
184    /// be used in production.
185    pub fn add_recording(self) -> Interpreter<M, Record<S>, Tx, Ecal> {
186        Interpreter {
187            registers: self.registers,
188            memory: self.memory,
189            frames: self.frames,
190            receipts: self.receipts,
191            tx: self.tx,
192            initial_balances: self.initial_balances,
193            input_contracts: self.input_contracts,
194            input_contracts_index_to_output_index: self
195                .input_contracts_index_to_output_index,
196            storage: Record::new(self.storage),
197            debugger: self.debugger,
198            context: self.context,
199            balances: self.balances,
200            panic_context: self.panic_context,
201            profiler: self.profiler,
202            interpreter_params: self.interpreter_params,
203            ecal_state: self.ecal_state,
204        }
205    }
206
207    /// Change this VMs internal state to match the initial state from this diff.
208    pub fn reset_vm_state(&mut self, diff: &Diff<InitialVmState>)
209    where
210        Tx: Clone + 'static,
211    {
212        for change in &diff.changes {
213            self.inverse_inner(change);
214            if let Change::Storage(Previous(from)) = change {
215                match from {
216                    StorageState::State(MappableState { key, value }) => {
217                        if let Some(value) = value {
218                            StorageMutate::<ContractsState>::insert(
219                                &mut self.storage,
220                                key,
221                                value.as_ref(),
222                            )
223                            .unwrap();
224                        }
225                    }
226                    StorageState::Assets(MappableState { key, value }) => {
227                        if let Some(value) = value {
228                            StorageMutate::<ContractsAssets>::insert(
229                                &mut self.storage,
230                                key,
231                                value,
232                            )
233                            .unwrap();
234                        }
235                    }
236                    StorageState::RawCode(MappableState { key, value }) => {
237                        if let Some(value) = value {
238                            StorageMutate::<ContractsRawCode>::insert(
239                                &mut self.storage,
240                                key,
241                                value.as_ref(),
242                            )
243                            .unwrap();
244                        }
245                    }
246                    StorageState::UploadedBytecode(MappableState { key, value }) => {
247                        if let Some(value) = value {
248                            StorageMutate::<UploadedBytecodes>::insert(
249                                &mut self.storage,
250                                key,
251                                value,
252                            )
253                            .unwrap();
254                        }
255                    }
256                    StorageState::BlobData(MappableState { key, value }) => {
257                        if let Some(value) = value {
258                            StorageMutate::<BlobData>::insert(
259                                &mut self.storage,
260                                key,
261                                value.as_ref(),
262                            )
263                            .unwrap();
264                        }
265                    }
266                }
267            }
268        }
269    }
270}
271
272fn mappable_delta_to_hashmap<'value, K, V>(
273    state: &mut Delta<HashMap<K, &'value V>>,
274    delta: &'value MappableDelta<K, V>,
275) where
276    K: Copy + PartialEq + Eq + core::hash::Hash + 'static,
277    V: Clone + 'static,
278{
279    match delta {
280        MappableDelta::Replace(key, value, Some(existing)) => {
281            state.from.entry(*key).or_insert(existing);
282            state.to.insert(*key, value);
283        }
284        MappableDelta::Replace(key, value, None) => {
285            state.to.insert(*key, value);
286        }
287        MappableDelta::Take(key, existing) => {
288            state.from.entry(*key).or_insert(existing);
289            state.to.remove(key);
290        }
291    }
292}
293
294fn storage_state_to_changes<K, V>(
295    diff: &mut Diff<Deltas>,
296    state: Delta<HashMap<K, &V>>,
297    f: fn(MappableState<K, V>) -> StorageState,
298) where
299    K: Copy + PartialEq + Eq + Hash + 'static,
300    V: Clone + 'static,
301{
302    let Delta { mut from, to } = state;
303    let iter = to.into_iter().map(|(k, v)| {
304        Change::Storage(Delta {
305            from: f(MappableState {
306                key: k,
307                value: from.remove(&k).cloned(),
308            }),
309            to: f(MappableState {
310                key: k,
311                value: Some(v.clone()),
312            }),
313        })
314    });
315    diff.changes.extend(iter);
316    let iter = from.into_iter().map(|(k, v)| {
317        Change::Storage(Delta {
318            from: f(MappableState {
319                key: k,
320                value: Some(v.clone()),
321            }),
322            to: f(MappableState {
323                key: k,
324                value: None,
325            }),
326        })
327    });
328    diff.changes.extend(iter);
329}
330
331impl<Type: Mappable, S> StorageInspect<Type> for Record<S>
332where
333    S: StorageInspect<Type>,
334    S: InterpreterStorage,
335{
336    type Error = <S as StorageInspect<Type>>::Error;
337
338    fn get(
339        &self,
340        key: &<Type as Mappable>::Key,
341    ) -> Result<Option<alloc::borrow::Cow<<Type as Mappable>::OwnedValue>>, Self::Error>
342    {
343        <S as StorageInspect<Type>>::get(&self.0, key)
344    }
345
346    fn contains_key(&self, key: &<Type as Mappable>::Key) -> Result<bool, Self::Error> {
347        <S as StorageInspect<Type>>::contains_key(&self.0, key)
348    }
349}
350
351impl<Type: Mappable, S> StorageSize<Type> for Record<S>
352where
353    S: StorageSize<Type>,
354    S: InterpreterStorage,
355{
356    fn size_of_value(
357        &self,
358        key: &<Type as Mappable>::Key,
359    ) -> Result<Option<usize>, Self::Error> {
360        <S as StorageSize<Type>>::size_of_value(&self.0, key)
361    }
362}
363
364impl<Type: Mappable, S> StorageRead<Type> for Record<S>
365where
366    S: StorageRead<Type>,
367    S: InterpreterStorage,
368{
369    fn read(
370        &self,
371        key: &<Type as Mappable>::Key,
372        offset: usize,
373        buf: &mut [u8],
374    ) -> Result<Option<usize>, Self::Error> {
375        <S as StorageRead<Type>>::read(&self.0, key, offset, buf)
376    }
377
378    fn read_alloc(
379        &self,
380        key: &<Type as Mappable>::Key,
381    ) -> Result<Option<Vec<u8>>, Self::Error> {
382        <S as StorageRead<Type>>::read_alloc(&self.0, key)
383    }
384}
385
386impl<Type: StorageType, S> StorageMutate<Type> for Record<S>
387where
388    S: StorageInspect<Type>,
389    S: StorageMutate<Type>,
390    S: InterpreterStorage,
391{
392    fn replace(
393        &mut self,
394        key: &Type::Key,
395        value: &Type::Value,
396    ) -> Result<Option<Type::OwnedValue>, Self::Error> {
397        let existing = <S as StorageMutate<Type>>::replace(&mut self.0, key, value)?;
398        self.1.push(<Type as StorageType>::record_replace(
399            key,
400            value,
401            existing.clone(),
402        ));
403        Ok(existing)
404    }
405
406    fn take(&mut self, key: &Type::Key) -> Result<Option<Type::OwnedValue>, Self::Error> {
407        let existing = <S as StorageMutate<Type>>::take(&mut self.0, key)?;
408        if let Some(existing) = &existing {
409            self.1
410                .push(<Type as StorageType>::record_take(key, existing.clone()));
411        }
412        Ok(existing)
413    }
414}
415
416impl<Type: StorageType, S> StorageWrite<Type> for Record<S>
417where
418    S: StorageWrite<Type>,
419    S: InterpreterStorage,
420{
421    fn write_bytes(&mut self, key: &Type::Key, buf: &[u8]) -> Result<usize, Self::Error> {
422        <S as StorageWrite<Type>>::write_bytes(&mut self.0, key, buf)
423    }
424
425    fn replace_bytes(
426        &mut self,
427        key: &Type::Key,
428        buf: &[u8],
429    ) -> Result<(usize, Option<Vec<u8>>), Self::Error> {
430        <S as StorageWrite<Type>>::replace_bytes(&mut self.0, key, buf)
431    }
432
433    fn take_bytes(&mut self, key: &Type::Key) -> Result<Option<Vec<u8>>, Self::Error> {
434        <S as StorageWrite<Type>>::take_bytes(&mut self.0, key)
435    }
436}
437
438impl<S: ContractsAssetsStorage + InterpreterStorage> ContractsAssetsStorage
439    for Record<S>
440{
441}
442
443impl<S> InterpreterStorage for Record<S>
444where
445    S: InterpreterStorage,
446{
447    type DataError = <S as InterpreterStorage>::DataError;
448
449    fn block_height(&self) -> Result<BlockHeight, Self::DataError> {
450        self.0.block_height()
451    }
452
453    fn consensus_parameters_version(&self) -> Result<u32, Self::DataError> {
454        self.0.consensus_parameters_version()
455    }
456
457    fn state_transition_version(&self) -> Result<u32, Self::DataError> {
458        self.0.state_transition_version()
459    }
460
461    fn timestamp(&self, height: BlockHeight) -> Result<Word, Self::DataError> {
462        self.0.timestamp(height)
463    }
464
465    fn block_hash(&self, block_height: BlockHeight) -> Result<Bytes32, Self::DataError> {
466        self.0.block_hash(block_height)
467    }
468
469    fn coinbase(&self) -> Result<fuel_types::ContractId, Self::DataError> {
470        self.0.coinbase()
471    }
472
473    fn set_consensus_parameters(
474        &mut self,
475        version: u32,
476        consensus_parameters: &ConsensusParameters,
477    ) -> Result<Option<ConsensusParameters>, Self::DataError> {
478        self.0
479            .set_consensus_parameters(version, consensus_parameters)
480    }
481
482    fn set_state_transition_bytecode(
483        &mut self,
484        version: u32,
485        hash: &Bytes32,
486    ) -> Result<Option<Bytes32>, Self::DataError> {
487        self.0.set_state_transition_bytecode(version, hash)
488    }
489
490    fn contract_state_range(
491        &self,
492        id: &ContractId,
493        start_key: &Bytes32,
494        range: usize,
495    ) -> Result<Vec<Option<alloc::borrow::Cow<ContractsStateData>>>, Self::DataError>
496    {
497        self.0.contract_state_range(id, start_key, range)
498    }
499
500    fn contract_state_insert_range<'a, I>(
501        &mut self,
502        contract: &ContractId,
503        start_key: &Bytes32,
504        values: I,
505    ) -> Result<usize, Self::DataError>
506    where
507        I: Iterator<Item = &'a [u8]>,
508    {
509        self.0
510            .contract_state_insert_range(contract, start_key, values)
511    }
512
513    fn contract_state_remove_range(
514        &mut self,
515        contract: &ContractId,
516        start_key: &Bytes32,
517        range: usize,
518    ) -> Result<Option<()>, S::DataError> {
519        self.0
520            .contract_state_remove_range(contract, start_key, range)
521    }
522}
523
524impl StorageType for ContractsState {
525    fn record_replace(
526        key: &Self::Key,
527        value: &[u8],
528        existing: Option<ContractsStateData>,
529    ) -> StorageDelta {
530        StorageDelta::State(MappableDelta::Replace(*key, value.into(), existing))
531    }
532
533    fn record_take(key: &Self::Key, value: ContractsStateData) -> StorageDelta {
534        StorageDelta::State(MappableDelta::Take(*key, value))
535    }
536}
537
538impl StorageType for ContractsAssets {
539    fn record_replace(
540        key: &Self::Key,
541        value: &u64,
542        existing: Option<u64>,
543    ) -> StorageDelta {
544        StorageDelta::Assets(MappableDelta::Replace(*key, *value, existing))
545    }
546
547    fn record_take(key: &Self::Key, value: u64) -> StorageDelta {
548        StorageDelta::Assets(MappableDelta::Take(*key, value))
549    }
550}
551
552impl StorageType for ContractsRawCode {
553    fn record_replace(
554        key: &ContractId,
555        value: &[u8],
556        existing: Option<Contract>,
557    ) -> StorageDelta {
558        StorageDelta::RawCode(MappableDelta::Replace(*key, value.into(), existing))
559    }
560
561    fn record_take(key: &ContractId, value: Contract) -> StorageDelta {
562        StorageDelta::RawCode(MappableDelta::Take(*key, value))
563    }
564}
565
566impl StorageType for UploadedBytecodes {
567    fn record_replace(
568        key: &Bytes32,
569        value: &UploadedBytecode,
570        existing: Option<UploadedBytecode>,
571    ) -> StorageDelta {
572        StorageDelta::UploadedBytecode(MappableDelta::Replace(
573            *key,
574            value.clone(),
575            existing,
576        ))
577    }
578
579    fn record_take(key: &Bytes32, value: UploadedBytecode) -> StorageDelta {
580        StorageDelta::UploadedBytecode(MappableDelta::Take(*key, value))
581    }
582}
583
584impl StorageType for BlobData {
585    fn record_replace(
586        key: &BlobId,
587        value: &[u8],
588        existing: Option<BlobBytes>,
589    ) -> StorageDelta {
590        StorageDelta::BlobData(MappableDelta::Replace(*key, value.into(), existing))
591    }
592
593    fn record_take(key: &BlobId, value: BlobBytes) -> StorageDelta {
594        StorageDelta::BlobData(MappableDelta::Take(*key, value))
595    }
596}
597impl<S> Record<S>
598where
599    S: InterpreterStorage,
600{
601    pub fn new(s: S) -> Self {
602        Self(s, Vec::new())
603    }
604}