casper_types/system/
caller.rs

1pub mod call_stack_elements;
2
3use alloc::{collections::BTreeMap, vec::Vec};
4
5use num_derive::{FromPrimitive, ToPrimitive};
6use num_traits::FromPrimitive;
7
8use crate::{
9    account::AccountHash,
10    bytesrepr::{self, FromBytes, ToBytes, U8_SERIALIZED_LENGTH},
11    package::PackageHash,
12    CLType, CLTyped, CLValue, CLValueError, EntityAddr, HashAddr,
13};
14
15use crate::{
16    bytesrepr::Error,
17    contracts::{ContractHash, ContractPackageHash},
18};
19pub use call_stack_elements::CallStackElement;
20
21/// Tag representing variants of CallerTag for purposes of serialization.
22#[derive(FromPrimitive, ToPrimitive)]
23#[repr(u8)]
24pub enum CallerTag {
25    /// Initiator tag.
26    Initiator = 0,
27    /// Entity tag.
28    Entity,
29    /// Smart contract tag.
30    SmartContract,
31}
32
33const ACCOUNT: u8 = 0;
34const PACKAGE: u8 = 1;
35const CONTRACT_PACKAGE: u8 = 2;
36const ENTITY: u8 = 3;
37const CONTRACT: u8 = 4;
38
39#[derive(Clone, Debug, PartialEq, Eq)]
40pub struct CallerInfo {
41    kind: u8,
42    fields: BTreeMap<u8, CLValue>,
43}
44
45impl CLTyped for CallerInfo {
46    fn cl_type() -> CLType {
47        CLType::Any
48    }
49}
50
51impl CallerInfo {
52    pub fn kind(&self) -> u8 {
53        self.kind
54    }
55
56    pub fn get_field_by_index(&self, index: u8) -> Option<&CLValue> {
57        self.fields.get(&index)
58    }
59}
60
61impl ToBytes for CallerInfo {
62    fn to_bytes(&self) -> Result<Vec<u8>, Error> {
63        let mut result = bytesrepr::allocate_buffer(self)?;
64        result.append(&mut self.kind.to_bytes()?);
65        result.append(&mut self.fields.to_bytes()?);
66        Ok(result)
67    }
68
69    fn serialized_length(&self) -> usize {
70        U8_SERIALIZED_LENGTH + self.fields.serialized_length()
71    }
72}
73
74impl FromBytes for CallerInfo {
75    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error> {
76        let (kind, remainder) = u8::from_bytes(bytes)?;
77        let (fields, remainder) = BTreeMap::from_bytes(remainder)?;
78        Ok((CallerInfo { kind, fields }, remainder))
79    }
80}
81
82impl TryFrom<Caller> for CallerInfo {
83    type Error = CLValueError;
84
85    fn try_from(value: Caller) -> Result<Self, Self::Error> {
86        match value {
87            Caller::Initiator { account_hash } => {
88                let kind = ACCOUNT;
89
90                let mut ret = BTreeMap::new();
91                ret.insert(ACCOUNT, CLValue::from_t(Some(account_hash))?);
92                ret.insert(PACKAGE, CLValue::from_t(Option::<PackageHash>::None)?);
93                ret.insert(
94                    CONTRACT_PACKAGE,
95                    CLValue::from_t(Option::<ContractPackageHash>::None)?,
96                );
97                ret.insert(ENTITY, CLValue::from_t(Option::<EntityAddr>::None)?);
98                ret.insert(CONTRACT, CLValue::from_t(Option::<ContractHash>::None)?);
99                Ok(CallerInfo { kind, fields: ret })
100            }
101            Caller::Entity {
102                package_hash,
103                entity_addr,
104            } => {
105                let kind = ENTITY;
106
107                let mut ret = BTreeMap::new();
108                ret.insert(ACCOUNT, CLValue::from_t(Option::<AccountHash>::None)?);
109                ret.insert(PACKAGE, CLValue::from_t(Some(package_hash))?);
110                ret.insert(
111                    CONTRACT_PACKAGE,
112                    CLValue::from_t(Option::<ContractPackageHash>::None)?,
113                );
114                ret.insert(ENTITY, CLValue::from_t(Some(entity_addr))?);
115                ret.insert(CONTRACT, CLValue::from_t(Option::<ContractHash>::None)?);
116                Ok(CallerInfo { kind, fields: ret })
117            }
118            Caller::SmartContract {
119                contract_package_hash,
120                contract_hash,
121            } => {
122                let kind = CONTRACT;
123
124                let mut ret = BTreeMap::new();
125                ret.insert(ACCOUNT, CLValue::from_t(Option::<AccountHash>::None)?);
126                ret.insert(PACKAGE, CLValue::from_t(Option::<PackageHash>::None)?);
127                ret.insert(
128                    CONTRACT_PACKAGE,
129                    CLValue::from_t(Some(contract_package_hash))?,
130                );
131
132                ret.insert(ENTITY, CLValue::from_t(Option::<EntityAddr>::None)?);
133                ret.insert(CONTRACT, CLValue::from_t(Some(contract_hash))?);
134                Ok(CallerInfo { kind, fields: ret })
135            }
136        }
137    }
138}
139
140/// Identity of a calling entity.
141#[derive(Clone, Copy, Debug, PartialEq, Eq)]
142pub enum Caller {
143    /// Initiator (calling account)
144    Initiator {
145        /// The account hash of the caller
146        account_hash: AccountHash,
147    },
148    /// Entity (smart contract / system contract)
149    Entity {
150        /// The package hash
151        package_hash: PackageHash,
152        /// The entity addr.
153        entity_addr: EntityAddr,
154    },
155    SmartContract {
156        /// The contract package hash.
157        contract_package_hash: ContractPackageHash,
158        /// The contract hash.
159        contract_hash: ContractHash,
160    },
161}
162
163impl Caller {
164    /// Creates a [`Caller::Initiator`]. This represents a call into session code, and
165    /// should only ever happen once in a call stack.
166    pub fn initiator(account_hash: AccountHash) -> Self {
167        Caller::Initiator { account_hash }
168    }
169
170    /// Creates a [`'Caller::Entity`]. This represents a call into a contract with
171    /// `EntryPointType::Called`.
172    pub fn entity(package_hash: PackageHash, entity_addr: EntityAddr) -> Self {
173        Caller::Entity {
174            package_hash,
175            entity_addr,
176        }
177    }
178
179    pub fn smart_contract(
180        contract_package_hash: ContractPackageHash,
181        contract_hash: ContractHash,
182    ) -> Self {
183        Caller::SmartContract {
184            contract_package_hash,
185            contract_hash,
186        }
187    }
188
189    /// Gets the tag from self.
190    pub fn tag(&self) -> CallerTag {
191        match self {
192            Caller::Initiator { .. } => CallerTag::Initiator,
193            Caller::Entity { .. } => CallerTag::Entity,
194            Caller::SmartContract { .. } => CallerTag::SmartContract,
195        }
196    }
197
198    /// Gets the [`HashAddr`] for both stored session and stored contract variants.
199    pub fn contract_hash(&self) -> Option<HashAddr> {
200        match self {
201            Caller::Initiator { .. } => None,
202            Caller::Entity { entity_addr, .. } => Some(entity_addr.value()),
203            Caller::SmartContract { contract_hash, .. } => Some(contract_hash.value()),
204        }
205    }
206}
207
208impl ToBytes for Caller {
209    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
210        let mut result = bytesrepr::allocate_buffer(self)?;
211        result.push(self.tag() as u8);
212        match self {
213            Caller::Initiator { account_hash } => result.append(&mut account_hash.to_bytes()?),
214
215            Caller::Entity {
216                package_hash,
217                entity_addr,
218            } => {
219                result.append(&mut package_hash.to_bytes()?);
220                result.append(&mut entity_addr.to_bytes()?);
221            }
222            Caller::SmartContract {
223                contract_package_hash,
224                contract_hash,
225            } => {
226                result.append(&mut contract_package_hash.to_bytes()?);
227                result.append(&mut contract_hash.to_bytes()?);
228            }
229        };
230        Ok(result)
231    }
232
233    fn serialized_length(&self) -> usize {
234        U8_SERIALIZED_LENGTH
235            + match self {
236                Caller::Initiator { account_hash } => account_hash.serialized_length(),
237                Caller::Entity {
238                    package_hash,
239                    entity_addr,
240                } => package_hash.serialized_length() + entity_addr.serialized_length(),
241                Caller::SmartContract {
242                    contract_package_hash,
243                    contract_hash,
244                } => contract_package_hash.serialized_length() + contract_hash.serialized_length(),
245            }
246    }
247}
248
249impl FromBytes for Caller {
250    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
251        let (tag, remainder): (u8, &[u8]) = FromBytes::from_bytes(bytes)?;
252        let tag = CallerTag::from_u8(tag).ok_or(bytesrepr::Error::Formatting)?;
253        match tag {
254            CallerTag::Initiator => {
255                let (account_hash, remainder) = AccountHash::from_bytes(remainder)?;
256                Ok((Caller::Initiator { account_hash }, remainder))
257            }
258            CallerTag::Entity => {
259                let (package_hash, remainder) = PackageHash::from_bytes(remainder)?;
260                let (entity_addr, remainder) = EntityAddr::from_bytes(remainder)?;
261                Ok((
262                    Caller::Entity {
263                        package_hash,
264                        entity_addr,
265                    },
266                    remainder,
267                ))
268            }
269            CallerTag::SmartContract => {
270                let (contract_package_hash, remainder) =
271                    ContractPackageHash::from_bytes(remainder)?;
272                let (contract_hash, remainder) = ContractHash::from_bytes(remainder)?;
273                Ok((
274                    Caller::SmartContract {
275                        contract_package_hash,
276                        contract_hash,
277                    },
278                    remainder,
279                ))
280            }
281        }
282    }
283}
284
285impl CLTyped for Caller {
286    fn cl_type() -> CLType {
287        CLType::Any
288    }
289}
290
291impl From<&Caller> for CallStackElement {
292    fn from(caller: &Caller) -> Self {
293        match caller {
294            Caller::Initiator { account_hash } => CallStackElement::Session {
295                account_hash: *account_hash,
296            },
297            Caller::Entity {
298                package_hash,
299                entity_addr: entity_hash,
300            } => CallStackElement::StoredContract {
301                contract_package_hash: ContractPackageHash::new(package_hash.value()),
302                contract_hash: ContractHash::new(entity_hash.value()),
303            },
304            Caller::SmartContract {
305                contract_package_hash,
306                contract_hash,
307            } => CallStackElement::StoredContract {
308                contract_package_hash: *contract_package_hash,
309                contract_hash: *contract_hash,
310            },
311        }
312    }
313}