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
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 obj.write_xdr(&mut w)
65 .map_err(|_| (ScErrorType::Budget, ScErrorCode::ExceededLimit).into())
66}
67
68pub 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}