fuel_vm/
call.rs

1//! Inter-contract call supporting structures
2
3use fuel_asm::{
4    PanicReason,
5    RegId,
6};
7use fuel_types::{
8    bytes::padded_len_usize,
9    canonical::{
10        Deserialize,
11        Serialize,
12    },
13    AssetId,
14    ContractId,
15    Word,
16};
17
18use crate::consts::{
19    WORD_SIZE,
20    *,
21};
22
23#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25#[derive(Deserialize, Serialize)]
26/// Call structure representation, composed of a called contract `to` and two
27/// word arguments.
28///
29/// <https://github.com/FuelLabs/fuel-specs/blob/master/src/fuel-vm/instruction-set.md#call-call-contract>
30pub struct Call {
31    to: ContractId,
32    a: Word,
33    b: Word,
34}
35
36impl Call {
37    /// The size of the call structures in memory representation.
38    pub const LEN: usize = ContractId::LEN + 8 + 8;
39
40    /// Create a new call structure representation.
41    pub const fn new(to: ContractId, a: Word, b: Word) -> Self {
42        Self { to, a, b }
43    }
44
45    /// Called contract.
46    pub const fn to(&self) -> &ContractId {
47        &self.to
48    }
49
50    /// `a` argument.
51    pub const fn a(&self) -> Word {
52        self.a
53    }
54
55    /// `b` argument.
56    pub const fn b(&self) -> Word {
57        self.b
58    }
59
60    /// Expose the internal attributes of the call description.
61    pub const fn into_inner(self) -> (ContractId, Word, Word) {
62        (self.to, self.a, self.b)
63    }
64}
65
66#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
67/// Call frame representation in the VM stack.
68///
69/// <https://github.com/FuelLabs/fuel-specs/blob/master/src/fuel-vm/index.md#call-frames>
70pub struct CallFrame {
71    to: ContractId,
72    asset_id: AssetId,
73    registers: [Word; VM_REGISTER_COUNT],
74    code_size_padded: usize,
75    a: Word,
76    b: Word,
77}
78
79#[cfg(test)]
80impl Default for CallFrame {
81    fn default() -> Self {
82        Self {
83            to: ContractId::default(),
84            asset_id: AssetId::default(),
85            registers: [0; VM_REGISTER_COUNT],
86            code_size_padded: 0,
87            a: 0,
88            b: 0,
89        }
90    }
91}
92
93impl CallFrame {
94    /// Create a new call frame.
95    pub fn new(
96        to: ContractId,
97        asset_id: AssetId,
98        registers: [Word; VM_REGISTER_COUNT],
99        code_size: usize,
100        a: Word,
101        b: Word,
102    ) -> Option<Self> {
103        Some(Self {
104            to,
105            asset_id,
106            registers,
107            code_size_padded: padded_len_usize(code_size)?,
108            a,
109            b,
110        })
111    }
112
113    /// Start of the contract id offset from the beginning of the call frame.
114    pub const fn contract_id_offset() -> usize {
115        0
116    }
117
118    /// Start of the asset id offset from the beginning of the call frame.
119    pub const fn asset_id_offset() -> usize {
120        Self::contract_id_offset().saturating_add(ContractId::LEN)
121    }
122
123    /// Start of the registers offset from the beginning of the call frame.
124    pub const fn registers_offset() -> usize {
125        Self::asset_id_offset().saturating_add(AssetId::LEN)
126    }
127
128    /// Start of the code size offset from the beginning of the call frame.
129    pub const fn code_size_offset() -> usize {
130        Self::registers_offset().saturating_add(WORD_SIZE * VM_REGISTER_COUNT)
131    }
132
133    /// Start of the `a` argument offset from the beginning of the call frame.
134    pub const fn a_offset() -> usize {
135        Self::code_size_offset().saturating_add(WORD_SIZE)
136    }
137
138    /// Start of the `b` argument offset from the beginning of the call frame.
139    pub const fn b_offset() -> usize {
140        Self::a_offset().saturating_add(WORD_SIZE)
141    }
142
143    /// Size of the call frame in bytes.
144    pub const fn serialized_size() -> usize {
145        Self::b_offset().saturating_add(WORD_SIZE)
146    }
147
148    /// Registers prior to the called execution.
149    pub const fn registers(&self) -> &[Word] {
150        &self.registers
151    }
152
153    /// Called contract id.
154    pub const fn to(&self) -> &ContractId {
155        &self.to
156    }
157
158    #[cfg(feature = "test-helpers")]
159    /// Contract code length in bytes.
160    pub fn code_size_padded(&self) -> usize {
161        self.code_size_padded
162    }
163
164    /// `a` argument.
165    pub const fn a(&self) -> Word {
166        self.a
167    }
168
169    /// `b` argument.
170    pub const fn b(&self) -> Word {
171        self.b
172    }
173
174    /// Gas context prior to the called execution.
175    pub fn context_gas(&self) -> Word {
176        self.registers[RegId::CGAS]
177    }
178
179    /// Asset ID of forwarded coins.
180    pub const fn asset_id(&self) -> &AssetId {
181        &self.asset_id
182    }
183
184    /// Returns the mutable value of the context gas for this call frame.
185    pub fn context_gas_mut(&mut self) -> &mut Word {
186        &mut self.registers[RegId::CGAS]
187    }
188
189    /// Returns the mutable value of the global gas for this call frame.
190    pub fn global_gas_mut(&mut self) -> &mut Word {
191        &mut self.registers[RegId::GGAS]
192    }
193}
194
195impl TryFrom<&[u8]> for Call {
196    type Error = PanicReason;
197
198    fn try_from(mut value: &[u8]) -> Result<Self, Self::Error> {
199        Self::decode(&mut value).map_err(|_| PanicReason::MalformedCallStructure)
200    }
201}
202
203#[cfg(test)]
204impl From<Call> for alloc::vec::Vec<u8> {
205    fn from(call: Call) -> Self {
206        call.to_bytes()
207    }
208}
209
210#[cfg(test)]
211impl From<CallFrame> for alloc::vec::Vec<u8> {
212    fn from(call: CallFrame) -> Self {
213        call.to_bytes()
214    }
215}