fuels_programs/
executable.rs1use fuels_core::{
2 types::{
3 errors::Result,
4 transaction_builders::{Blob, BlobTransactionBuilder},
5 },
6 Configurables,
7};
8
9use crate::{
10 assembly::script_and_predicate_loader::{extract_data_offset, LoaderCode},
11 DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE,
12};
13
14#[derive(Debug, Clone, PartialEq)]
16pub struct Regular {
17 code: Vec<u8>,
18 configurables: Configurables,
19}
20
21impl Regular {
22 pub fn new(code: Vec<u8>, configurables: Configurables) -> Self {
23 Self {
24 code,
25 configurables,
26 }
27 }
28}
29
30#[derive(Debug, Clone, PartialEq)]
34pub struct Executable<State> {
35 state: State,
36}
37
38impl Executable<Regular> {
39 pub fn from_bytes(code: Vec<u8>) -> Self {
40 Executable {
41 state: Regular::new(code, Default::default()),
42 }
43 }
44
45 pub fn load_from(path: &str) -> Result<Executable<Regular>> {
55 let code = std::fs::read(path)?;
56
57 Ok(Executable {
58 state: Regular::new(code, Default::default()),
59 })
60 }
61
62 pub fn with_configurables(self, configurables: impl Into<Configurables>) -> Self {
63 Executable {
64 state: Regular {
65 configurables: configurables.into(),
66 ..self.state
67 },
68 }
69 }
70
71 pub fn data_offset_in_code(&self) -> Result<usize> {
72 extract_data_offset(&self.state.code)
73 }
74
75 pub fn code(&self) -> Vec<u8> {
81 let mut code = self.state.code.clone();
82 self.state.configurables.update_constants_in(&mut code);
83 code
84 }
85
86 pub fn convert_to_loader(self) -> Result<Executable<Loader>> {
93 validate_loader_can_be_made_from_code(
94 self.state.code.clone(),
95 self.state.configurables.clone(),
96 )?;
97
98 Ok(Executable {
99 state: Loader {
100 code: self.state.code,
101 configurables: self.state.configurables,
102 },
103 })
104 }
105}
106
107pub struct Loader {
108 code: Vec<u8>,
109 configurables: Configurables,
110}
111
112impl Executable<Loader> {
113 pub fn with_configurables(self, configurables: impl Into<Configurables>) -> Self {
114 Executable {
115 state: Loader {
116 configurables: configurables.into(),
117 ..self.state
118 },
119 }
120 }
121
122 pub fn data_offset_in_code(&self) -> usize {
123 self.loader_code().data_section_offset()
124 }
125
126 fn loader_code(&self) -> LoaderCode {
127 let mut code = self.state.code.clone();
128
129 self.state.configurables.update_constants_in(&mut code);
130
131 LoaderCode::from_normal_binary(code)
132 .expect("checked before turning into a Executable<Loader>")
133 }
134
135 pub fn code(&self) -> Vec<u8> {
137 self.loader_code().as_bytes().to_vec()
138 }
139
140 pub fn blob(&self) -> Blob {
142 LoaderCode::extract_blob(&self.state.code)
145 .expect("checked before turning into a Executable<Loader>")
146 }
147
148 pub async fn upload_blob(&self, account: impl fuels_accounts::Account) -> Result<()> {
150 let blob = self.blob();
151 let provider = account.try_provider()?;
152
153 if provider.blob_exists(blob.id()).await? {
154 return Ok(());
155 }
156
157 let mut tb = BlobTransactionBuilder::default()
158 .with_blob(self.blob())
159 .with_max_fee_estimation_tolerance(DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE);
160
161 account.adjust_for_fee(&mut tb, 0).await?;
162
163 account.add_witnesses(&mut tb)?;
164
165 let tx = tb.build(provider).await?;
166
167 provider
168 .send_transaction_and_await_commit(tx)
169 .await?
170 .check(None)?;
171
172 Ok(())
173 }
174}
175
176fn validate_loader_can_be_made_from_code(
177 mut code: Vec<u8>,
178 configurables: Configurables,
179) -> Result<()> {
180 configurables.update_constants_in(&mut code);
181
182 let _ = LoaderCode::from_normal_binary(code)?;
183
184 Ok(())
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190 use fuels_core::Configurables;
191 use std::io::Write;
192 use tempfile::NamedTempFile;
193
194 #[test]
195 fn test_executable_regular_from_bytes() {
196 let code = vec![1u8, 2, 3, 4];
198
199 let executable = Executable::<Regular>::from_bytes(code.clone());
201
202 assert_eq!(executable.state.code, code);
204 assert_eq!(executable.state.configurables, Default::default());
205 }
206
207 #[test]
208 fn test_executable_regular_load_from() {
209 let code = vec![5u8, 6, 7, 8];
211 let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
212 temp_file
213 .write_all(&code)
214 .expect("Failed to write to temp file");
215 let path = temp_file.path().to_str().unwrap();
216
217 let executable_result = Executable::<Regular>::load_from(path);
219
220 assert!(executable_result.is_ok());
222 let executable = executable_result.unwrap();
223 assert_eq!(executable.state.code, code);
224 assert_eq!(executable.state.configurables, Default::default());
225 }
226
227 #[test]
228 fn test_executable_regular_load_from_invalid_path() {
229 let invalid_path = "/nonexistent/path/to/file";
231
232 let executable_result = Executable::<Regular>::load_from(invalid_path);
234
235 assert!(executable_result.is_err());
237 }
238
239 #[test]
240 fn test_executable_regular_with_configurables() {
241 let code = vec![1u8, 2, 3, 4];
243 let executable = Executable::<Regular>::from_bytes(code);
244 let configurables = Configurables::new(vec![(2, vec![1])]);
245
246 let new_executable = executable.with_configurables(configurables.clone());
248
249 assert_eq!(new_executable.state.configurables, configurables);
251 }
252
253 #[test]
254 fn test_executable_regular_code() {
255 let code = vec![1u8, 2, 3, 4];
257 let configurables = Configurables::new(vec![(1, vec![1])]);
258 let executable =
259 Executable::<Regular>::from_bytes(code.clone()).with_configurables(configurables);
260
261 let modified_code = executable.code();
263
264 assert_eq!(modified_code, vec![1, 1, 3, 4]);
265 }
266
267 #[test]
268 fn test_loader_extracts_code_and_data_section_correctly() {
269 let padding = vec![0; 8];
271 let offset = 20u64.to_be_bytes().to_vec();
272 let some_random_instruction = vec![1, 2, 3, 4];
273 let data_section = vec![5, 6, 7, 8];
274 let code = [
275 padding.clone(),
276 offset.clone(),
277 some_random_instruction.clone(),
278 data_section,
279 ]
280 .concat();
281 let executable = Executable::<Regular>::from_bytes(code.clone());
282
283 let loader = executable.convert_to_loader().unwrap();
285
286 let blob = loader.blob();
287 let data_stripped_code = [padding, offset, some_random_instruction].concat();
288 assert_eq!(blob.as_ref(), data_stripped_code);
289
290 let loader_code = loader.code();
291 assert_eq!(
292 loader_code,
293 LoaderCode::from_normal_binary(code).unwrap().as_bytes()
294 )
295 }
296
297 #[test]
298 fn test_executable_regular_convert_to_loader_with_invalid_code() {
299 let code = vec![1u8, 2]; let executable = Executable::<Regular>::from_bytes(code);
302
303 let result = executable.convert_to_loader();
305
306 assert!(result.is_err());
308 }
309
310 #[test]
311 fn executable_with_no_data_section() {
312 let data_section_offset = 16u64;
315
316 let code = [vec![0; 8], data_section_offset.to_be_bytes().to_vec()].concat();
317
318 Executable::from_bytes(code).convert_to_loader().unwrap();
319 }
320}