fuel_vm/interpreter/
initialization.rs1use 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 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 self.registers.iter_mut().for_each(|r| *r = 0);
84
85 self.registers[RegId::ONE] = 1;
86
87 self.registers[RegId::HP] = VM_MAX_RAM;
89
90 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 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 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}