fuel_vm/interpreter/
initialization.rs

1use super::{
2    ExecutableTransaction,
3    InitialBalances,
4    Interpreter,
5    Memory,
6    RuntimeBalances,
7};
8use crate::{
9    checked_transaction::{
10        IntoChecked,
11        Ready,
12    },
13    consts::*,
14    context::Context,
15    error::InterpreterError,
16    prelude::RuntimeError,
17    storage::InterpreterStorage,
18};
19use fuel_asm::RegId;
20use fuel_tx::{
21    field::{
22        Script,
23        ScriptGasLimit,
24    },
25    Input,
26    Output,
27};
28use fuel_types::Word;
29
30use crate::interpreter::CheckedMetadata;
31
32impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal>
33where
34    M: Memory,
35    Tx: ExecutableTransaction,
36    S: InterpreterStorage,
37{
38    /// Initialize the VM with a given transaction
39    fn init_inner(
40        &mut self,
41        mut tx: Tx,
42        initial_balances: InitialBalances,
43        runtime_balances: RuntimeBalances,
44        gas_limit: Word,
45    ) -> Result<(), RuntimeError<S::DataError>> {
46        tx.prepare_sign();
47        self.tx = tx;
48        self.input_contracts = self
49            .tx
50            .inputs()
51            .iter()
52            .filter_map(|i| match i {
53                Input::Contract(contract) => Some(contract.contract_id),
54                _ => None,
55            })
56            .collect();
57
58        self.input_contracts_index_to_output_index = self
59            .tx
60            .outputs()
61            .iter()
62            .enumerate()
63            .filter_map(|(output_idx, o)| match o {
64                Output::Contract(fuel_tx::output::contract::Contract {
65                    input_index,
66                    ..
67                }) => Some((
68                    *input_index,
69                    u16::try_from(output_idx)
70                        .expect("The maximum number of outputs is `u16::MAX`"),
71                )),
72                _ => None,
73            })
74            .collect();
75
76        self.initial_balances = initial_balances.clone();
77
78        self.frames.clear();
79        self.receipts.clear();
80        self.memory_mut().reset();
81
82        // Optimized for memset
83        self.registers.iter_mut().for_each(|r| *r = 0);
84
85        self.registers[RegId::ONE] = 1;
86
87        // Set heap area
88        self.registers[RegId::HP] = VM_MAX_RAM;
89
90        // Initialize stack
91        macro_rules! push_stack {
92            ($v:expr) => {{
93                let data = $v;
94                let old_ssp = self.registers[RegId::SSP];
95                let new_ssp = old_ssp
96                    .checked_add(data.len() as Word)
97                    .expect("VM initialization data must fit into the stack");
98                self.memory_mut().grow_stack(new_ssp)?;
99                self.registers[RegId::SSP] = new_ssp;
100                self.memory_mut()
101                    .write_noownerchecks(old_ssp, data.len())
102                    .expect("VM initialization data must fit into the stack")
103                    .copy_from_slice(data);
104            }};
105        }
106
107        push_stack!(&*self.transaction().id(&self.chain_id()));
108
109        let base_asset_id = self.interpreter_params.base_asset_id;
110        push_stack!(&*base_asset_id);
111
112        runtime_balances.to_vm(self);
113
114        let tx_size = self.transaction().size() as Word;
115        self.set_gas(gas_limit);
116
117        push_stack!(&tx_size.to_be_bytes());
118
119        let tx_bytes = self.tx.to_bytes();
120        push_stack!(tx_bytes.as_slice());
121
122        self.registers[RegId::SP] = self.registers[RegId::SSP];
123
124        Ok(())
125    }
126}
127
128impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal>
129where
130    M: Memory,
131    Tx: ExecutableTransaction,
132    S: InterpreterStorage,
133{
134    /// Initialize the VM for a predicate context
135    pub fn init_predicate(
136        &mut self,
137        context: Context,
138        tx: Tx,
139        gas_limit: Word,
140    ) -> Result<(), InterpreterError<S::DataError>> {
141        self.context = context;
142        let initial_balances: InitialBalances = Default::default();
143        let runtime_balances = initial_balances.clone().try_into()?;
144
145        let range = self
146            .context
147            .predicate()
148            .expect("The context is not predicate")
149            .program()
150            .words();
151
152        self.init_inner(tx, initial_balances, runtime_balances, gas_limit)?;
153        self.registers[RegId::PC] = range.start as fuel_asm::Word;
154        self.registers[RegId::IS] = range.start as fuel_asm::Word;
155
156        Ok(())
157    }
158}
159
160impl<M, S, Tx, Ecal> Interpreter<M, S, Tx, Ecal>
161where
162    M: Memory,
163    S: InterpreterStorage,
164    <S as InterpreterStorage>::DataError: From<S::DataError>,
165    Tx: ExecutableTransaction,
166    <Tx as IntoChecked>::Metadata: CheckedMetadata,
167{
168    /// Initialize the VM with a given transaction, backed by a storage provider that
169    /// allows execution of contract opcodes.
170    ///
171    /// For predicate estimation and verification, check [`Self::init_predicate`]
172    pub fn init_script(
173        &mut self,
174        ready_tx: Ready<Tx>,
175    ) -> Result<(), InterpreterError<S::DataError>> {
176        let block_height = self.storage.block_height().map_err(RuntimeError::Storage)?;
177
178        self.context = Context::Script { block_height };
179
180        let (_, checked) = ready_tx.decompose();
181        let (tx, metadata): (Tx, Tx::Metadata) = checked.into();
182
183        let gas_limit = tx
184            .as_script()
185            .map(|script| *script.script_gas_limit())
186            .unwrap_or_default();
187
188        let initial_balances = metadata.balances();
189        let runtime_balances = initial_balances.try_into()?;
190        self.init_inner(tx, metadata.balances(), runtime_balances, gas_limit)?;
191
192        if let Some(script) = self.transaction().as_script() {
193            let offset = self.tx_offset().saturating_add(script.script_offset()) as Word;
194
195            debug_assert!(offset < VM_MAX_RAM);
196
197            self.registers[RegId::PC] = offset;
198            self.registers[RegId::IS] = offset;
199        }
200
201        Ok(())
202    }
203}