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
9struct MeteredWrite<'a, W: Write> {
10    budget: &'a Budget,
11    w: &'a mut W,
12}
13
14impl<W> Write for MeteredWrite<'_, W>
15where
16    W: Write,
17{
18    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
19        self.budget
20            .charge(ContractCostType::ValSer, Some(buf.len() as u64))
21            .map_err(Into::<std::io::Error>::into)?;
22        self.w.write(buf)
23    }
24
25    fn flush(&mut self) -> std::io::Result<()> {
26        self.w.flush()
27    }
28}
29
30impl Host {
31    pub fn metered_hash_xdr(&self, obj: &impl WriteXdr) -> Result<[u8; 32], HostError> {
32        let _span = tracy_span!("hash xdr");
33        let mut buf = vec![];
34        metered_write_xdr(self.budget_ref(), obj, &mut buf)?;
35        sha256_hash_from_bytes_raw(&buf, self)
36    }
37
38    pub fn metered_from_xdr<T: ReadXdr>(&self, bytes: &[u8]) -> Result<T, HostError> {
39        let _span = tracy_span!("read xdr");
40        self.charge_budget(ContractCostType::ValDeser, Some(bytes.len() as u64))?;
41        let mut limits = DEFAULT_XDR_RW_LIMITS;
42        limits.len = bytes.len();
43        self.map_err(T::from_xdr(bytes, limits))
44    }
45
46    pub(crate) fn metered_from_xdr_obj<T: ReadXdr>(
47        &self,
48        bytes: BytesObject,
49    ) -> Result<T, HostError> {
50        self.visit_obj(bytes, |hv: &ScBytes| self.metered_from_xdr(hv.as_slice()))
51    }
52}
53
54pub fn metered_write_xdr(
55    budget: &Budget,
56    obj: &impl WriteXdr,
57    w: &mut Vec<u8>,
58) -> Result<(), HostError> {
59    let _span = tracy_span!("write xdr");
60    let mut w = Limited::new(MeteredWrite { budget, w }, DEFAULT_XDR_RW_LIMITS);
61    // MeteredWrite above turned any budget failure into an IO error; we turn it
62    // back to a budget failure here, since there's really no "IO error" that can
63    // occur when writing to a Vec<u8>.
64    obj.write_xdr(&mut w)
65        .map_err(|_| (ScErrorType::Budget, ScErrorCode::ExceededLimit).into())
66}
67
68// Host-less metered XDR decoding.
69// Prefer using `metered_from_xdr` when host is available for better error
70// reporting.
71pub fn metered_from_xdr_with_budget<T: ReadXdr>(
72    bytes: &[u8],
73    budget: &Budget,
74) -> Result<T, HostError> {
75    let _span = tracy_span!("read xdr with budget");
76    budget.charge(ContractCostType::ValDeser, Some(bytes.len() as u64))?;
77    let mut limits = DEFAULT_XDR_RW_LIMITS;
78    limits.len = bytes.len();
79    T::from_xdr(bytes, limits).map_err(|e| e.into())
80}