1use super::{
2 ExecutableTransaction,
3 ExecutableTxType,
4 Interpreter,
5 Memory,
6 internal::inc_pc,
7};
8use crate::{
9 call::CallFrame,
10 constraints::reg_key::*,
11 consts::*,
12 context::Context,
13 convert,
14 error::SimpleResult,
15};
16use fuel_asm::{
17 GMArgs,
18 GTFArgs,
19 PanicReason,
20 RegId,
21};
22use fuel_tx::{
23 Input,
24 InputRepr,
25 Output,
26 OutputRepr,
27 UtxoId,
28 field::{
29 BlobId,
30 BytecodeRoot,
31 BytecodeWitnessIndex,
32 ProofSet,
33 Salt,
34 Script as ScriptField,
35 ScriptData,
36 ScriptGasLimit,
37 StorageSlots,
38 SubsectionIndex,
39 SubsectionsNumber,
40 UpgradePurpose,
41 },
42 policies::PolicyType,
43};
44use fuel_types::{
45 ChainId,
46 Immediate12,
47 Immediate18,
48 Word,
49};
50
51#[cfg(test)]
52mod tests;
53
54impl<M, S, Tx, Ecal, V> Interpreter<M, S, Tx, Ecal, V>
55where
56 M: Memory,
57 Tx: ExecutableTransaction,
58{
59 pub(crate) fn metadata(&mut self, ra: RegId, imm: Immediate18) -> SimpleResult<()> {
60 let tx_offset = self.tx_offset() as Word;
61 let chain_id = self.chain_id();
62 let gas_price = self.gas_price();
63 let (SystemRegisters { pc, .. }, mut w) = split_registers(&mut self.registers);
64 let result = &mut w[WriteRegKey::try_from(ra)?];
65 metadata(
66 &self.context,
67 &self.frames,
68 pc,
69 result,
70 imm,
71 chain_id,
72 tx_offset,
73 gas_price,
74 self.owner_ptr,
75 )
76 }
77
78 pub(crate) fn get_transaction_field(
79 &mut self,
80 ra: RegId,
81 b: Word,
82 imm: Immediate12,
83 ) -> SimpleResult<()> {
84 let tx_offset = self.tx_offset();
85 let tx_size_ptr = tx_offset.checked_sub(8).expect("Tx offset is not valid");
87 let tx_size = Word::from_be_bytes(
88 self.memory()
89 .read_bytes(tx_size_ptr)
90 .expect("Tx length not in memory"),
91 );
92 let (SystemRegisters { pc, .. }, mut w) = split_registers(&mut self.registers);
93 let result = &mut w[WriteRegKey::try_from(ra)?];
94 let input = GTFInput {
95 tx: &self.tx,
96 input_contracts_index_to_output_index: &self
97 .input_contracts_index_to_output_index,
98 tx_offset,
99 tx_size,
100 pc,
101 };
102 input.get_transaction_field(result, b, imm)
103 }
104}
105
106#[allow(clippy::too_many_arguments)]
107pub(crate) fn metadata(
108 context: &Context,
109 frames: &[CallFrame],
110 pc: RegMut<PC>,
111 result: &mut Word,
112 imm: Immediate18,
113 chain_id: ChainId,
114 tx_offset: Word,
115 gas_price: Word,
116 owner_ptr: Option<Word>,
117) -> SimpleResult<()> {
118 let parent = context
119 .is_internal()
120 .then(|| frames.last().map(|f| f.registers()[RegId::FP]))
121 .flatten();
122
123 *result = match GMArgs::try_from(imm)? {
124 GMArgs::GetVerifyingPredicate => context
125 .predicate()
126 .map(|p| p.idx() as Word)
127 .ok_or(PanicReason::TransactionValidity)?,
128 GMArgs::GetChainId => chain_id.into(),
129 GMArgs::BaseAssetId => VM_MEMORY_BASE_ASSET_ID_OFFSET as Word,
130 GMArgs::TxStart => tx_offset,
131 GMArgs::GetCaller => match parent {
132 Some(0) => return Err(PanicReason::ExpectedNestedCaller.into()),
133 Some(parent) => parent,
134 None => return Err(PanicReason::ExpectedInternalContext.into()),
135 },
136 GMArgs::IsCallerExternal => match parent {
137 Some(p) => (p == 0) as Word,
138 None => return Err(PanicReason::ExpectedInternalContext.into()),
139 },
140 GMArgs::GetGasPrice => match context {
141 Context::PredicateVerification { .. }
142 | Context::PredicateEstimation { .. } => {
143 return Err(PanicReason::CanNotGetGasPriceInPredicate.into())
144 }
145 _ => gas_price,
146 },
147 GMArgs::GetOwner => match owner_ptr {
148 None => return Err(PanicReason::OwnerIsUnknown.into()),
149 Some(ptr) => ptr,
150 },
151 };
152
153 inc_pc(pc)?;
154 Ok(())
155}
156
157struct GTFInput<'vm, Tx> {
158 tx: &'vm Tx,
159 input_contracts_index_to_output_index: &'vm alloc::collections::BTreeMap<u16, u16>,
160 tx_offset: usize,
161 tx_size: Word,
162 pc: RegMut<'vm, PC>,
163}
164
165impl<Tx> GTFInput<'_, Tx> {
166 #[allow(deprecated)]
167 pub(crate) fn get_transaction_field(
168 self,
169 result: &mut Word,
170 b: Word,
171 imm: Immediate12,
172 ) -> SimpleResult<()>
173 where
174 Tx: ExecutableTransaction,
175 {
176 let b = convert::to_usize(b).ok_or(PanicReason::InvalidMetadataIdentifier)?;
177 let args = GTFArgs::try_from(imm)?;
178 let tx = self.tx;
179 let input_contract_to_output_index = self.input_contracts_index_to_output_index;
180 let ofs = self.tx_offset;
181
182 let a = match args {
187 GTFArgs::Type => Tx::transaction_type() as Word,
188
189 GTFArgs::ScriptGasLimit => tx
191 .as_script()
192 .map(|script| *script.script_gas_limit())
193 .unwrap_or_default(),
194 GTFArgs::PolicyTypes => tx.policies().bits() as Word,
195 GTFArgs::PolicyTip => tx
196 .policies()
197 .get(PolicyType::Tip)
198 .ok_or(PanicReason::PolicyIsNotSet)?,
199 GTFArgs::PolicyWitnessLimit => tx
200 .policies()
201 .get(PolicyType::WitnessLimit)
202 .ok_or(PanicReason::PolicyIsNotSet)?,
203 GTFArgs::PolicyMaturity => tx
204 .policies()
205 .get(PolicyType::Maturity)
206 .ok_or(PanicReason::PolicyIsNotSet)?,
207 GTFArgs::PolicyExpiration => tx
208 .policies()
209 .get(PolicyType::Expiration)
210 .ok_or(PanicReason::PolicyIsNotSet)?,
211 GTFArgs::PolicyMaxFee => tx
212 .policies()
213 .get(PolicyType::MaxFee)
214 .ok_or(PanicReason::PolicyIsNotSet)?,
215 GTFArgs::PolicyOwner => tx
216 .policies()
217 .get(PolicyType::Owner)
218 .ok_or(PanicReason::PolicyIsNotSet)?,
219 GTFArgs::ScriptInputsCount
220 | GTFArgs::CreateInputsCount
221 | GTFArgs::TxInputsCount => tx.inputs().len() as Word,
222 GTFArgs::ScriptOutputsCount
223 | GTFArgs::CreateOutputsCount
224 | GTFArgs::TxOutputsCount => tx.outputs().len() as Word,
225 GTFArgs::ScriptWitnessesCount
226 | GTFArgs::CreateWitnessesCount
227 | GTFArgs::TxWitnessesCount => tx.witnesses().len() as Word,
228 GTFArgs::ScriptInputAtIndex
229 | GTFArgs::CreateInputAtIndex
230 | GTFArgs::TxInputAtIndex => ofs
231 .saturating_add(tx.inputs_offset_at(b).ok_or(PanicReason::InputNotFound)?)
232 as Word,
233 GTFArgs::ScriptOutputAtIndex
234 | GTFArgs::CreateOutputAtIndex
235 | GTFArgs::TxOutputAtIndex => ofs.saturating_add(
236 tx.outputs_offset_at(b).ok_or(PanicReason::OutputNotFound)?,
237 ) as Word,
238 GTFArgs::ScriptWitnessAtIndex
239 | GTFArgs::CreateWitnessAtIndex
240 | GTFArgs::TxWitnessAtIndex => ofs.saturating_add(
241 tx.witnesses_offset_at(b)
242 .ok_or(PanicReason::WitnessNotFound)?,
243 ) as Word,
244 GTFArgs::TxLength => self.tx_size,
245
246 GTFArgs::InputType => {
248 tx.inputs()
249 .get(b)
250 .map(InputRepr::from)
251 .ok_or(PanicReason::InputNotFound)? as Word
252 }
253 GTFArgs::InputCoinTxId => ofs.saturating_add(
254 tx.inputs()
255 .get(b)
256 .filter(|i| i.is_coin())
257 .map(Input::repr)
258 .and_then(|r| r.utxo_id_offset())
259 .and_then(|ofs| tx.inputs_offset_at(b).map(|o| o.saturating_add(ofs)))
260 .ok_or(PanicReason::InputNotFound)?,
261 ) as Word,
262 GTFArgs::InputCoinOutputIndex => {
263 tx.inputs()
264 .get(b)
265 .filter(|i| i.is_coin())
266 .and_then(Input::utxo_id)
267 .map(UtxoId::output_index)
268 .ok_or(PanicReason::InputNotFound)? as Word
269 }
270 GTFArgs::InputCoinOwner => ofs.saturating_add(
271 tx.inputs()
272 .get(b)
273 .filter(|i| i.is_coin())
274 .map(Input::repr)
275 .and_then(|r| r.owner_offset())
276 .and_then(|ofs| tx.inputs_offset_at(b).map(|o| o.saturating_add(ofs)))
277 .ok_or(PanicReason::InputNotFound)?,
278 ) as Word,
279 GTFArgs::InputCoinAmount => tx
280 .inputs()
281 .get(b)
282 .filter(|i| i.is_coin())
283 .and_then(Input::amount)
284 .ok_or(PanicReason::InputNotFound)?,
285 GTFArgs::InputCoinAssetId => ofs.saturating_add(
286 tx.inputs()
287 .get(b)
288 .filter(|i| i.is_coin())
289 .map(Input::repr)
290 .and_then(|r| r.asset_id_offset())
291 .and_then(|ofs| tx.inputs_offset_at(b).map(|o| o.saturating_add(ofs)))
292 .ok_or(PanicReason::InputNotFound)?,
293 ) as Word,
294 GTFArgs::InputCoinTxPointer => ofs.saturating_add(
295 tx.inputs()
296 .get(b)
297 .filter(|i| i.is_coin())
298 .map(Input::repr)
299 .and_then(|r| r.tx_pointer_offset())
300 .and_then(|ofs| tx.inputs_offset_at(b).map(|o| o.saturating_add(ofs)))
301 .ok_or(PanicReason::InputNotFound)?,
302 ) as Word,
303 GTFArgs::InputCoinWitnessIndex => {
304 tx.inputs()
305 .get(b)
306 .filter(|i| i.is_coin())
307 .and_then(Input::witness_index)
308 .ok_or(PanicReason::InputNotFound)? as Word
309 }
310 GTFArgs::InputCoinPredicateLength => {
311 tx.inputs()
312 .get(b)
313 .filter(|i| i.is_coin())
314 .and_then(Input::predicate_len)
315 .ok_or(PanicReason::InputNotFound)? as Word
316 }
317 GTFArgs::InputCoinPredicateDataLength => {
318 tx.inputs()
319 .get(b)
320 .filter(|i| i.is_coin())
321 .and_then(Input::predicate_data_len)
322 .ok_or(PanicReason::InputNotFound)? as Word
323 }
324 GTFArgs::InputCoinPredicateGasUsed => {
325 tx.inputs()
326 .get(b)
327 .filter(|i| i.is_coin())
328 .and_then(Input::predicate_gas_used)
329 .ok_or(PanicReason::InputNotFound)? as Word
330 }
331 GTFArgs::InputCoinPredicate => ofs.saturating_add(
332 tx.inputs()
333 .get(b)
334 .filter(|i| i.is_coin())
335 .and_then(Input::predicate_offset)
336 .and_then(|ofs| tx.inputs_offset_at(b).map(|o| o.saturating_add(ofs)))
337 .ok_or(PanicReason::InputNotFound)?,
338 ) as Word,
339 GTFArgs::InputCoinPredicateData => ofs.saturating_add(
340 tx.inputs()
341 .get(b)
342 .filter(|i| i.is_coin())
343 .and_then(Input::predicate_data_offset)
344 .and_then(|ofs| tx.inputs_offset_at(b).map(|o| o.saturating_add(ofs)))
345 .ok_or(PanicReason::InputNotFound)?,
346 ) as Word,
347 GTFArgs::InputContractTxId => ofs.saturating_add(
348 tx.inputs()
349 .get(b)
350 .filter(|i| i.is_contract())
351 .map(Input::repr)
352 .and_then(|r| r.utxo_id_offset())
353 .and_then(|ofs| tx.inputs_offset_at(b).map(|o| o.saturating_add(ofs)))
354 .ok_or(PanicReason::InputNotFound)?,
355 ) as Word,
356 GTFArgs::InputContractOutputIndex => {
357 let b = u16::try_from(b)
358 .map_err(|_| PanicReason::InvalidMetadataIdentifier)?;
359 input_contract_to_output_index
360 .get(&b)
361 .copied()
362 .ok_or(PanicReason::InputNotFound)? as Word
363 }
364 GTFArgs::InputContractId => ofs.saturating_add(
365 tx.inputs()
366 .get(b)
367 .filter(|i| i.is_contract())
368 .map(Input::repr)
369 .and_then(|r| r.contract_id_offset())
370 .and_then(|ofs| tx.inputs_offset_at(b).map(|o| o.saturating_add(ofs)))
371 .ok_or(PanicReason::InputNotFound)?,
372 ) as Word,
373 GTFArgs::InputMessageSender => ofs.saturating_add(
374 tx.inputs()
375 .get(b)
376 .filter(|i| i.is_message())
377 .map(Input::repr)
378 .and_then(|r| r.message_sender_offset())
379 .and_then(|ofs| tx.inputs_offset_at(b).map(|o| o.saturating_add(ofs)))
380 .ok_or(PanicReason::InputNotFound)?,
381 ) as Word,
382 GTFArgs::InputMessageRecipient => ofs.saturating_add(
383 tx.inputs()
384 .get(b)
385 .filter(|i| i.is_message())
386 .map(Input::repr)
387 .and_then(|r| r.message_recipient_offset())
388 .and_then(|ofs| tx.inputs_offset_at(b).map(|o| o.saturating_add(ofs)))
389 .ok_or(PanicReason::InputNotFound)?,
390 ) as Word,
391 GTFArgs::InputMessageAmount => tx
392 .inputs()
393 .get(b)
394 .filter(|i| i.is_message())
395 .and_then(Input::amount)
396 .ok_or(PanicReason::InputNotFound)?,
397 GTFArgs::InputMessageNonce => ofs.saturating_add(
398 tx.inputs()
399 .get(b)
400 .filter(|i| i.is_message())
401 .map(Input::repr)
402 .and_then(|r| r.message_nonce_offset())
403 .and_then(|ofs| tx.inputs_offset_at(b).map(|o| o.saturating_add(ofs)))
404 .ok_or(PanicReason::InputNotFound)?,
405 ) as Word,
406 GTFArgs::InputMessageWitnessIndex => {
407 tx.inputs()
408 .get(b)
409 .filter(|i| i.is_message())
410 .and_then(Input::witness_index)
411 .ok_or(PanicReason::InputNotFound)? as Word
412 }
413 GTFArgs::InputMessageDataLength => {
414 tx.inputs()
415 .get(b)
416 .filter(|i| i.is_message())
417 .and_then(Input::input_data_len)
418 .ok_or(PanicReason::InputNotFound)? as Word
419 }
420 GTFArgs::InputMessagePredicateLength => {
421 tx.inputs()
422 .get(b)
423 .filter(|i| i.is_message())
424 .and_then(Input::predicate_len)
425 .ok_or(PanicReason::InputNotFound)? as Word
426 }
427 GTFArgs::InputMessagePredicateDataLength => {
428 tx.inputs()
429 .get(b)
430 .filter(|i| i.is_message())
431 .and_then(Input::predicate_data_len)
432 .ok_or(PanicReason::InputNotFound)? as Word
433 }
434 GTFArgs::InputMessagePredicateGasUsed => {
435 tx.inputs()
436 .get(b)
437 .filter(|i| i.is_message())
438 .and_then(Input::predicate_gas_used)
439 .ok_or(PanicReason::InputNotFound)? as Word
440 }
441 GTFArgs::InputMessageData => ofs.saturating_add(
442 tx.inputs()
443 .get(b)
444 .filter(|i| i.is_message())
445 .map(Input::repr)
446 .and_then(|r| r.data_offset())
447 .and_then(|ofs| tx.inputs_offset_at(b).map(|o| o.saturating_add(ofs)))
448 .ok_or(PanicReason::InputNotFound)?,
449 ) as Word,
450 GTFArgs::InputMessagePredicate => ofs.saturating_add(
451 tx.inputs()
452 .get(b)
453 .filter(|i| i.is_message())
454 .and_then(Input::predicate_offset)
455 .and_then(|ofs| tx.inputs_offset_at(b).map(|o| o.saturating_add(ofs)))
456 .ok_or(PanicReason::InputNotFound)?,
457 ) as Word,
458 GTFArgs::InputMessagePredicateData => ofs.saturating_add(
459 tx.inputs()
460 .get(b)
461 .filter(|i| i.is_message())
462 .and_then(Input::predicate_data_offset)
463 .and_then(|ofs| tx.inputs_offset_at(b).map(|o| o.saturating_add(ofs)))
464 .ok_or(PanicReason::InputNotFound)?,
465 ) as Word,
466
467 GTFArgs::OutputType => {
469 tx.outputs()
470 .get(b)
471 .map(OutputRepr::from)
472 .ok_or(PanicReason::OutputNotFound)? as Word
473 }
474 GTFArgs::OutputCoinTo => ofs.saturating_add(
475 tx.outputs()
476 .get(b)
477 .filter(|o| o.is_coin() || o.is_change())
478 .map(Output::repr)
479 .and_then(|r| r.to_offset())
480 .and_then(|ofs| {
481 tx.outputs_offset_at(b).map(|o| o.saturating_add(ofs))
482 })
483 .ok_or(PanicReason::OutputNotFound)?,
484 ) as Word,
485 GTFArgs::OutputCoinAmount => tx
486 .outputs()
487 .get(b)
488 .filter(|o| o.is_coin())
489 .and_then(Output::amount)
490 .ok_or(PanicReason::OutputNotFound)?,
491 GTFArgs::OutputCoinAssetId => ofs.saturating_add(
492 tx.outputs()
493 .get(b)
494 .filter(|o| o.is_coin() || o.is_change())
495 .map(Output::repr)
496 .and_then(|r| r.asset_id_offset())
497 .and_then(|ofs| {
498 tx.outputs_offset_at(b).map(|o| o.saturating_add(ofs))
499 })
500 .ok_or(PanicReason::OutputNotFound)?,
501 ) as Word,
502 GTFArgs::OutputContractInputIndex => {
503 tx.outputs()
504 .get(b)
505 .filter(|o| o.is_contract())
506 .and_then(Output::input_index)
507 .ok_or(PanicReason::InputNotFound)? as Word
508 }
509 GTFArgs::OutputContractCreatedContractId => ofs.saturating_add(
510 tx.outputs()
511 .get(b)
512 .filter(|o| o.is_contract_created())
513 .map(Output::repr)
514 .and_then(|r| r.contract_id_offset())
515 .and_then(|ofs| {
516 tx.outputs_offset_at(b).map(|o| o.saturating_add(ofs))
517 })
518 .ok_or(PanicReason::OutputNotFound)?,
519 ) as Word,
520 GTFArgs::OutputContractCreatedStateRoot => ofs.saturating_add(
521 tx.outputs()
522 .get(b)
523 .filter(|o| o.is_contract_created())
524 .map(Output::repr)
525 .and_then(|r| r.contract_created_state_root_offset())
526 .and_then(|ofs| {
527 tx.outputs_offset_at(b).map(|o| o.saturating_add(ofs))
528 })
529 .ok_or(PanicReason::OutputNotFound)?,
530 ) as Word,
531
532 GTFArgs::WitnessDataLength => {
534 tx.witnesses()
535 .get(b)
536 .map(|w| w.as_ref().len())
537 .ok_or(PanicReason::WitnessNotFound)? as Word
538 }
539 GTFArgs::WitnessData => {
540 tx.witnesses_offset_at(b)
541 .map(|w| ofs.saturating_add(w).saturating_add(WORD_SIZE))
542 .ok_or(PanicReason::WitnessNotFound)? as Word
543 }
544
545 specific_args => {
548 match (tx.executable_type(), specific_args) {
549 (ExecutableTxType::Script(script), GTFArgs::ScriptLength) => {
551 script.script().len() as Word
552 }
553 (ExecutableTxType::Script(script), GTFArgs::ScriptDataLength) => {
554 script.script_data().len() as Word
555 }
556 (ExecutableTxType::Script(script), GTFArgs::Script) => {
557 ofs.saturating_add(script.script_offset()) as Word
558 }
559 (ExecutableTxType::Script(script), GTFArgs::ScriptData) => {
560 ofs.saturating_add(script.script_data_offset()) as Word
561 }
562
563 (
565 ExecutableTxType::Create(create),
566 GTFArgs::CreateBytecodeWitnessIndex,
567 ) => *create.bytecode_witness_index() as Word,
568 (
569 ExecutableTxType::Create(create),
570 GTFArgs::CreateStorageSlotsCount,
571 ) => create.storage_slots().len() as Word,
572 (ExecutableTxType::Create(create), GTFArgs::CreateSalt) => {
573 ofs.saturating_add(create.salt_offset()) as Word
574 }
575 (
576 ExecutableTxType::Create(create),
577 GTFArgs::CreateStorageSlotAtIndex,
578 ) => {
579 (ofs.saturating_add(
580 create
581 .storage_slots_offset_at(b)
582 .ok_or(PanicReason::StorageSlotsNotFound)?,
583 )) as Word
584 }
585
586 (ExecutableTxType::Blob(blob), GTFArgs::BlobId) => {
588 ofs.saturating_add(blob.blob_id_offset()) as Word
589 }
590 (ExecutableTxType::Blob(blob), GTFArgs::BlobWitnessIndex) => {
591 *blob.bytecode_witness_index() as Word
592 }
593
594 (ExecutableTxType::Upload(upload), GTFArgs::UploadRoot) => {
596 ofs.saturating_add(upload.bytecode_root_offset()) as Word
597 }
598 (ExecutableTxType::Upload(upload), GTFArgs::UploadWitnessIndex) => {
599 *upload.bytecode_witness_index() as Word
600 }
601 (
602 ExecutableTxType::Upload(upload),
603 GTFArgs::UploadSubsectionIndex,
604 ) => *upload.subsection_index() as Word,
605 (
606 ExecutableTxType::Upload(upload),
607 GTFArgs::UploadSubsectionsCount,
608 ) => *upload.subsections_number() as Word,
609 (ExecutableTxType::Upload(upload), GTFArgs::UploadProofSetCount) => {
610 upload.proof_set().len() as Word
611 }
612 (
613 ExecutableTxType::Upload(upload),
614 GTFArgs::UploadProofSetAtIndex,
615 ) => ofs.saturating_add(
616 upload
617 .proof_set_offset_at(b)
618 .ok_or(PanicReason::ProofInUploadNotFound)?,
619 ) as Word,
620
621 (ExecutableTxType::Upgrade(upgrade), GTFArgs::UpgradePurpose) => {
623 ofs.saturating_add(upgrade.upgrade_purpose_offset()) as Word
624 }
625
626 _ => return Err(PanicReason::InvalidMetadataIdentifier.into()),
627 }
628 }
629 };
630
631 *result = a;
632
633 inc_pc(self.pc)?;
634 Ok(())
635 }
636}