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