1#![allow(dead_code)]
2
3use crate::{
4 budget::Budget,
5 host::{
6 metered_clone::{self, MeteredClone},
7 metered_map::MeteredOrdMap,
8 metered_vector::MeteredVector,
9 },
10 num::{I256, U256},
11 xdr::{self, ContractCostType, ScErrorCode, ScErrorType, SCSYMBOL_LIMIT},
12 AddressObject, BytesObject, Compare, DurationObject, DurationSmall, Host, HostError,
13 I128Object, I128Small, I256Object, I256Small, I64Object, I64Small, MapObject, Object,
14 StringObject, SymbolObject, SymbolSmall, SymbolStr, TimepointObject, TimepointSmall,
15 TryFromVal, U128Object, U128Small, U256Object, U256Small, U64Object, U64Small, Val, VecObject,
16};
17
18pub(crate) type HostMap = MeteredOrdMap<Val, Val, Host>;
19pub(crate) type HostVec = MeteredVector<Val>;
20
21#[derive(Clone, Hash)]
22pub enum HostObject {
23 Vec(HostVec),
24 Map(HostMap),
25 U64(u64),
26 I64(i64),
27 TimePoint(xdr::TimePoint),
28 Duration(xdr::Duration),
29 U128(u128),
30 I128(i128),
31 U256(U256),
32 I256(I256),
33 Bytes(xdr::ScBytes),
34 String(xdr::ScString),
35 Symbol(xdr::ScSymbol),
36 Address(xdr::ScAddress),
37}
38
39impl std::fmt::Debug for HostObject {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 match self {
42 Self::Vec(arg0) => f.debug_tuple("Vec").field(&arg0.len()).finish(),
43 Self::Map(arg0) => f.debug_tuple("Map").field(&arg0.len()).finish(),
44 Self::U64(arg0) => f.debug_tuple("U64").field(arg0).finish(),
45 Self::I64(arg0) => f.debug_tuple("I64").field(arg0).finish(),
46 Self::TimePoint(arg0) => f.debug_tuple("TimePoint").field(arg0).finish(),
47 Self::Duration(arg0) => f.debug_tuple("Duration").field(arg0).finish(),
48 Self::U128(arg0) => f.debug_tuple("U128").field(arg0).finish(),
49 Self::I128(arg0) => f.debug_tuple("I128").field(arg0).finish(),
50 Self::U256(arg0) => f.debug_tuple("U256").field(arg0).finish(),
51 Self::I256(arg0) => f.debug_tuple("I256").field(arg0).finish(),
52 Self::Bytes(arg0) => f.debug_tuple("Bytes").field(arg0).finish(),
53 Self::String(arg0) => f.debug_tuple("String").field(arg0).finish(),
54 Self::Symbol(arg0) => f.debug_tuple("Symbol").field(arg0).finish(),
55 Self::Address(arg0) => f.debug_tuple("Address").field(arg0).finish(),
56 }
57 }
58}
59
60impl HostObject {
61 pub(crate) fn try_compare_to_small(
65 &self,
66 budget: &Budget,
67 rv: Val,
68 ) -> Result<Option<core::cmp::Ordering>, HostError> {
69 let res = match self {
70 HostObject::U64(u) => {
71 let Ok(small) = U64Small::try_from(rv) else {
72 return Ok(None);
73 };
74 let small: u64 = small.into();
75 Some(budget.compare(u, &small)?)
76 }
77 HostObject::I64(i) => {
78 let Ok(small) = I64Small::try_from(rv) else {
79 return Ok(None);
80 };
81 let small: i64 = small.into();
82 Some(budget.compare(i, &small)?)
83 }
84 HostObject::TimePoint(tp) => {
85 let Ok(small) = TimepointSmall::try_from(rv) else {
86 return Ok(None);
87 };
88 let small: u64 = small.into();
89 Some(budget.compare(&tp.0, &small)?)
90 }
91 HostObject::Duration(d) => {
92 let Ok(small) = DurationSmall::try_from(rv) else {
93 return Ok(None);
94 };
95 let small: u64 = small.into();
96 Some(budget.compare(&d.0, &small)?)
97 }
98 HostObject::U128(u) => {
99 let Ok(small) = U128Small::try_from(rv) else {
100 return Ok(None);
101 };
102 let small: u128 = small.into();
103 Some(budget.compare(u, &small)?)
104 }
105 HostObject::I128(i) => {
106 let Ok(small) = I128Small::try_from(rv) else {
107 return Ok(None);
108 };
109 let small: i128 = small.into();
110 Some(budget.compare(i, &small)?)
111 }
112 HostObject::U256(u) => {
113 let Ok(small) = U256Small::try_from(rv) else {
114 return Ok(None);
115 };
116 let small: U256 = small.into();
117 Some(budget.compare(u, &small)?)
118 }
119 HostObject::I256(i) => {
120 let Ok(small) = I256Small::try_from(rv) else {
121 return Ok(None);
122 };
123 let small: I256 = small.into();
124 Some(budget.compare(i, &small)?)
125 }
126 HostObject::Symbol(s) => {
127 let Ok(small) = SymbolSmall::try_from(rv) else {
128 return Ok(None);
129 };
130 let small: SymbolStr = small.into();
131 let rhs: &[u8] = small.as_ref();
132 Some(budget.compare(&s.as_vec().as_slice(), &rhs)?)
133 }
134
135 HostObject::Vec(_)
136 | HostObject::Map(_)
137 | HostObject::Bytes(_)
138 | HostObject::String(_)
139 | HostObject::Address(_) => None,
140 };
141 Ok(res)
142 }
143}
144
145pub trait HostObjectType: MeteredClone {
146 type Wrapper: Into<Object>;
147 fn new_from_handle(handle: u32) -> Self::Wrapper;
148 fn inject(self) -> HostObject;
149 fn try_extract(obj: &HostObject) -> Option<&Self>;
150}
151
152pub(crate) trait MemHostObjectType:
155 HostObjectType + TryFrom<Vec<u8>, Error = xdr::Error> + Into<Vec<u8>>
156{
157 fn try_from_bytes(host: &Host, bytes: Vec<u8>) -> Result<Self, HostError>;
158 fn as_byte_slice(&self) -> &[u8];
159}
160
161macro_rules! declare_host_object_type {
162 ($TY:ty, $TAG:ident, $CASE:ident) => {
163 impl HostObjectType for $TY {
164 type Wrapper = $TAG;
165 fn new_from_handle(handle: u32) -> Self::Wrapper {
166 unsafe { $TAG::from_handle(handle) }
167 }
168 fn inject(self) -> HostObject {
169 HostObject::$CASE(self)
170 }
171
172 fn try_extract(obj: &HostObject) -> Option<&Self> {
173 match obj {
174 HostObject::$CASE(v) => Some(v),
175 _ => None,
176 }
177 }
178 }
179 };
180}
181
182macro_rules! declare_mem_host_object_type {
183 ($TY:ty, $TAG:ident, $CASE:ident) => {
184 declare_host_object_type!($TY, $TAG, $CASE);
185 impl MemHostObjectType for $TY {
186 fn try_from_bytes(_host: &Host, bytes: Vec<u8>) -> Result<Self, HostError> {
187 Self::try_from(bytes).map_err(Into::into)
188 }
189
190 fn as_byte_slice(&self) -> &[u8] {
191 self.as_slice()
192 }
193 }
194 };
195}
196
197declare_host_object_type!(HostMap, MapObject, Map);
199declare_host_object_type!(HostVec, VecObject, Vec);
200declare_host_object_type!(u64, U64Object, U64);
201declare_host_object_type!(i64, I64Object, I64);
202declare_host_object_type!(xdr::TimePoint, TimepointObject, TimePoint);
203declare_host_object_type!(xdr::Duration, DurationObject, Duration);
204declare_host_object_type!(u128, U128Object, U128);
205declare_host_object_type!(i128, I128Object, I128);
206declare_host_object_type!(U256, U256Object, U256);
207declare_host_object_type!(I256, I256Object, I256);
208declare_mem_host_object_type!(xdr::ScBytes, BytesObject, Bytes);
209declare_mem_host_object_type!(xdr::ScString, StringObject, String);
210declare_host_object_type!(xdr::ScSymbol, SymbolObject, Symbol);
211declare_host_object_type!(xdr::ScAddress, AddressObject, Address);
212
213impl MemHostObjectType for xdr::ScSymbol {
214 fn try_from_bytes(host: &Host, bytes: Vec<u8>) -> Result<Self, HostError> {
215 if bytes.len() as u64 > SCSYMBOL_LIMIT {
216 return Err(host.err(
217 ScErrorType::Value,
218 ScErrorCode::InvalidInput,
219 "slice is too long to be represented as Symbol",
220 &[(bytes.len() as u32).into()],
221 ));
222 }
223 for b in &bytes {
224 SymbolSmall::validate_byte(*b).map_err(|_| {
225 host.err(
226 ScErrorType::Value,
227 ScErrorCode::InvalidInput,
228 "byte is not allowed in Symbol",
229 &[(*b as u32).into()],
230 )
231 })?;
232 }
233 Self::try_from(bytes).map_err(Into::into)
234 }
235 fn as_byte_slice(&self) -> &[u8] {
236 self.as_ref()
237 }
238}
239
240pub fn is_relative_object_handle(handle: u32) -> bool {
273 handle & 1 == 0
274}
275
276pub fn handle_to_index(handle: u32) -> usize {
277 (handle as usize) >> 1
278}
279
280pub fn index_to_handle(host: &Host, index: usize, relative: bool) -> Result<u32, HostError> {
281 if let Ok(smaller) = u32::try_from(index) {
282 if let Some(shifted) = smaller.checked_shl(1) {
283 if relative {
284 return Ok(shifted);
285 } else {
286 return Ok(shifted | 1);
287 }
288 }
289 }
290 Err(host.err_arith_overflow())
291}
292
293impl Host {
294 pub fn relative_to_absolute(&self, val: Val) -> Result<Val, HostError> {
295 if let Ok(obj) = Object::try_from(val) {
296 let handle = obj.get_handle();
297 return if is_relative_object_handle(handle) {
298 let index = handle_to_index(handle);
299 let abs_opt = self.with_current_frame_relative_object_table(|table| {
300 Ok(table.get(index).map(|x| *x))
301 })?;
302 match abs_opt {
303 Some(abs) if abs.to_val().get_tag() == val.get_tag() => Ok(abs.into()),
304 Some(_) => Err(self.err(
316 ScErrorType::Value,
317 ScErrorCode::InvalidInput,
318 "relative and absolute object types differ",
319 &[],
320 )),
321 None => Err(self.err(
324 ScErrorType::Value,
325 ScErrorCode::InvalidInput,
326 "unknown relative object reference",
327 &[Val::from_u32(handle).to_val()],
328 )),
329 }
330 } else {
331 Err(self.err(
334 ScErrorType::Value,
335 ScErrorCode::InvalidInput,
336 "relative_to_absolute given an absolute reference",
337 &[Val::from_u32(handle).to_val()],
338 ))
339 };
340 }
341 Ok(val)
342 }
343
344 pub fn absolute_to_relative(&self, val: Val) -> Result<Val, HostError> {
345 if let Ok(obj) = Object::try_from(val) {
346 let handle = obj.get_handle();
347 return if is_relative_object_handle(handle) {
348 Err(self.err(
352 ScErrorType::Context,
353 ScErrorCode::InternalError,
354 "absolute_to_relative given a relative reference",
355 &[Val::from_u32(handle).to_val()],
359 ))
360 } else {
361 metered_clone::charge_heap_alloc::<Object>(1, self)?;
363 let index = self.with_current_frame_relative_object_table(|table| {
364 let index = table.len();
365 table.push(obj);
366 Ok(index)
367 })?;
368 let handle = index_to_handle(self, index, true)?;
369 Ok(Object::from_handle_and_tag(handle, val.get_tag()).into())
370 };
371 }
372 Ok(val)
373 }
374
375 pub fn add_host_object<HOT: HostObjectType>(
379 &self,
380 hot: HOT,
381 ) -> Result<HOT::Wrapper, HostError> {
382 let _span = tracy_span!("add host object");
383 let index = self.try_borrow_objects()?.len();
384 let handle = index_to_handle(self, index, false)?;
385 metered_clone::charge_heap_alloc::<HostObject>(1, self)?;
388 self.try_borrow_objects_mut()?.push(HOT::inject(hot));
389 Ok(HOT::new_from_handle(handle))
390 }
391
392 pub(crate) fn visit_obj_untyped<F, U>(
393 &self,
394 obj: impl Into<Object>,
395 f: F,
396 ) -> Result<U, HostError>
397 where
398 F: FnOnce(&HostObject) -> Result<U, HostError>,
399 {
400 let _span = tracy_span!("visit host object");
401 self.charge_budget(ContractCostType::VisitObject, None)?;
408 let r = self.try_borrow_objects()?;
409 let obj: Object = obj.into();
410 let handle: u32 = obj.get_handle();
411 if is_relative_object_handle(handle) {
412 Err(self.err(
415 ScErrorType::Object,
416 ScErrorCode::InternalError,
417 "looking up relative object",
418 &[Val::from_u32(handle).to_val()],
419 ))
420 } else if let Some(obj) = r.get(handle_to_index(handle)) {
421 f(obj)
422 } else {
423 let obj_payload = obj.as_val().get_payload();
429 let payload_val = Val::try_from_val(self, &obj_payload)?;
430 Err(self.err(
431 ScErrorType::Value,
432 ScErrorCode::InvalidInput,
433 "unknown object reference",
434 &[payload_val],
435 ))
436 }
437 }
438
439 pub(crate) fn visit_obj<HOT: HostObjectType, F, U>(
442 &self,
443 obj: HOT::Wrapper,
444 f: F,
445 ) -> Result<U, HostError>
446 where
447 F: FnOnce(&HOT) -> Result<U, HostError>,
448 {
449 self.visit_obj_untyped(obj, |hobj| match HOT::try_extract(hobj) {
450 None => Err(self.err(
453 xdr::ScErrorType::Object,
454 xdr::ScErrorCode::InternalError,
455 "object reference type does not match tag",
456 &[],
457 )),
458 Some(hot) => f(hot),
459 })
460 }
461}