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