soroban_env_host/host/
metered_xdr.rs1use crate::{
2 budget::Budget,
3 crypto::sha256_hash_from_bytes_raw,
4 xdr::{ContractCostType, Limited, ReadXdr, ScBytes, ScErrorCode, ScErrorType, WriteXdr},
5 BytesObject, Host, HostError, DEFAULT_XDR_RW_LIMITS,
6};
7use std::io::Write;
8
9use super::ErrorHandler;
10
11struct MeteredWrite<'a, W: Write> {
12 budget: &'a Budget,
13 w: &'a mut W,
14}
15
16impl<W> Write for MeteredWrite<'_, W>
17where
18 W: Write,
19{
20 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
21 self.budget
22 .charge(ContractCostType::ValSer, Some(buf.len() as u64))
23 .map_err(Into::<std::io::Error>::into)?;
24 self.w.write(buf)
25 }
26
27 fn flush(&mut self) -> std::io::Result<()> {
28 self.w.flush()
29 }
30}
31
32impl Host {
33 pub fn metered_hash_xdr(&self, obj: &impl WriteXdr) -> Result<[u8; 32], HostError> {
34 let _span = tracy_span!("hash xdr");
35 let mut buf = vec![];
36 metered_write_xdr(self.budget_ref(), obj, &mut buf)?;
37 sha256_hash_from_bytes_raw(&buf, self)
38 }
39
40 pub fn metered_from_xdr<T: ReadXdr>(&self, bytes: &[u8]) -> Result<T, HostError> {
41 let _span = tracy_span!("read xdr");
42 self.charge_budget(ContractCostType::ValDeser, Some(bytes.len() as u64))?;
43 let mut limits = DEFAULT_XDR_RW_LIMITS;
44 limits.len = bytes.len();
45 self.map_err(T::from_xdr(bytes, limits))
46 }
47
48 pub(crate) fn metered_from_xdr_obj<T: ReadXdr>(
49 &self,
50 bytes: BytesObject,
51 ) -> Result<T, HostError> {
52 self.visit_obj(bytes, |hv: &ScBytes| self.metered_from_xdr(hv.as_slice()))
53 }
54}
55
56pub fn metered_write_xdr(
57 budget: &Budget,
58 obj: &impl WriteXdr,
59 w: &mut Vec<u8>,
60) -> Result<(), HostError> {
61 let _span = tracy_span!("write xdr");
62 let mut w = Limited::new(MeteredWrite { budget, w }, DEFAULT_XDR_RW_LIMITS);
63 obj.write_xdr(&mut w)
67 .map_err(|_| (ScErrorType::Budget, ScErrorCode::ExceededLimit).into())
68}
69
70pub fn metered_from_xdr_with_budget<T: ReadXdr>(
74 bytes: &[u8],
75 budget: &Budget,
76) -> Result<T, HostError> {
77 let _span = tracy_span!("read xdr with budget");
78 budget.charge(ContractCostType::ValDeser, Some(bytes.len() as u64))?;
79 let mut limits = DEFAULT_XDR_RW_LIMITS;
80 limits.len = bytes.len();
81 T::from_xdr(bytes, limits).map_err(|e| e.into())
82}