soroban_env_host/host/
metered_xdr.rs

1use 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    // MeteredWrite above turned any budget failure into an IO error; we turn it
64    // back to a budget failure here, since there's really no "IO error" that can
65    // occur when writing to a Vec<u8>.
66    obj.write_xdr(&mut w)
67        .map_err(|_| (ScErrorType::Budget, ScErrorCode::ExceededLimit).into())
68}
69
70// Host-less metered XDR decoding.
71// Prefer using `metered_from_xdr` when host is available for better error
72// reporting.
73pub 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}