soroban_env_host/
budget.rs

1mod dimension;
2mod limits;
3mod model;
4mod util;
5mod wasmi_helper;
6
7pub(crate) use limits::DepthLimiter;
8pub use limits::{DEFAULT_HOST_DEPTH_LIMIT, DEFAULT_XDR_RW_LIMITS};
9pub use model::{MeteredCostComponent, ScaledU64};
10pub(crate) use wasmi_helper::{get_wasmi_config, load_calibrated_fuel_costs};
11
12use std::{
13    cell::{RefCell, RefMut},
14    fmt::{Debug, Display},
15    rc::Rc,
16};
17
18use crate::{
19    host::error::TryBorrowOrErr,
20    xdr::{ContractCostParams, ContractCostType, ScErrorCode, ScErrorType},
21    Error, Host, HostError,
22};
23
24use dimension::{BudgetDimension, IsCpu, IsShadowMode};
25
26#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
27pub struct CostTracker {
28    pub iterations: u64,
29    pub inputs: Option<u64>,
30    pub cpu: u64,
31    pub mem: u64,
32}
33
34#[derive(Clone)]
35struct BudgetTracker {
36    // Tracker for each `CostType`
37    cost_trackers: [CostTracker; ContractCostType::variants().len()],
38    // Total number of times the meter is called
39    meter_count: u32,
40    #[cfg(any(test, feature = "testutils", feature = "bench"))]
41    wasm_memory: u64,
42    // Tracks the real time (in nsecs) spent on various `CostType`
43    time_tracker: [u64; ContractCostType::variants().len()],
44}
45
46impl Default for BudgetTracker {
47    fn default() -> Self {
48        let mut mt = Self {
49            cost_trackers: [CostTracker::default(); ContractCostType::variants().len()],
50            meter_count: 0,
51            #[cfg(any(test, feature = "testutils", feature = "bench"))]
52            wasm_memory: 0,
53            time_tracker: [0; ContractCostType::variants().len()],
54        };
55        for (ct, tracker) in ContractCostType::variants()
56            .iter()
57            .zip(mt.cost_trackers.iter_mut())
58        {
59            // Define what inputs actually mean. For any constant-cost types --
60            // whether it is a true constant unit cost type, or empirically
61            // assigned (via measurement) constant type -- we leave the input as
62            // `None`, otherwise, we initialize the input to `Some(0)``.
63            let mut init_input = || tracker.inputs = Some(0);
64            match ct {
65                ContractCostType::WasmInsnExec => (),
66                ContractCostType::MemAlloc => init_input(), // number of bytes in host memory to allocate
67                ContractCostType::MemCpy => init_input(),   // number of bytes in host to copy
68                ContractCostType::MemCmp => init_input(),   // number of bytes in host to compare
69                ContractCostType::DispatchHostFunction => (),
70                ContractCostType::VisitObject => (),
71                // The inputs for `ValSer` and `ValDeser` are subtly different:
72                // `ValSer` works recursively via `WriteXdr`, and each leaf call charges the budget,
73                // and the input is the number of bytes of a leaf entity.
74                // `ValDeser` charges the budget at the top level. Call to `read_xdr` works through
75                // the bytes buffer recursively without worrying about budget charging. So the input
76                // is the length of the total buffer.
77                // This has implication on how their calibration should be set up.
78                ContractCostType::ValSer => init_input(), // number of bytes in the result buffer
79                ContractCostType::ValDeser => init_input(), // number of bytes in the input buffer
80                ContractCostType::ComputeSha256Hash => init_input(), // number of bytes in the buffer
81                ContractCostType::ComputeEd25519PubKey => (),
82                ContractCostType::VerifyEd25519Sig => init_input(), // length of the signed message
83                ContractCostType::VmInstantiation => init_input(),  // length of the wasm bytes,
84                ContractCostType::VmCachedInstantiation => init_input(), // length of the wasm bytes,
85                ContractCostType::InvokeVmFunction => (),
86                ContractCostType::ComputeKeccak256Hash => init_input(), // number of bytes in the buffer
87                ContractCostType::DecodeEcdsaCurve256Sig => (),
88                ContractCostType::RecoverEcdsaSecp256k1Key => (),
89                ContractCostType::Int256AddSub => (),
90                ContractCostType::Int256Mul => (),
91                ContractCostType::Int256Div => (),
92                ContractCostType::Int256Pow => (),
93                ContractCostType::Int256Shift => (),
94                ContractCostType::ChaCha20DrawBytes => init_input(), // number of random bytes to draw
95
96                ContractCostType::ParseWasmInstructions => init_input(),
97                ContractCostType::ParseWasmFunctions => init_input(),
98                ContractCostType::ParseWasmGlobals => init_input(),
99                ContractCostType::ParseWasmTableEntries => init_input(),
100                ContractCostType::ParseWasmTypes => init_input(),
101                ContractCostType::ParseWasmDataSegments => init_input(),
102                ContractCostType::ParseWasmElemSegments => init_input(),
103                ContractCostType::ParseWasmImports => init_input(),
104                ContractCostType::ParseWasmExports => init_input(),
105                ContractCostType::ParseWasmDataSegmentBytes => init_input(),
106                ContractCostType::InstantiateWasmInstructions => (),
107                ContractCostType::InstantiateWasmFunctions => init_input(),
108                ContractCostType::InstantiateWasmGlobals => init_input(),
109                ContractCostType::InstantiateWasmTableEntries => init_input(),
110                ContractCostType::InstantiateWasmTypes => (),
111                ContractCostType::InstantiateWasmDataSegments => init_input(),
112                ContractCostType::InstantiateWasmElemSegments => init_input(),
113                ContractCostType::InstantiateWasmImports => init_input(),
114                ContractCostType::InstantiateWasmExports => init_input(),
115                ContractCostType::InstantiateWasmDataSegmentBytes => init_input(),
116                ContractCostType::Sec1DecodePointUncompressed => (),
117                ContractCostType::VerifyEcdsaSecp256r1Sig => (),
118                ContractCostType::Bls12381EncodeFp => (),
119                ContractCostType::Bls12381DecodeFp => (),
120                ContractCostType::Bls12381G1CheckPointOnCurve => (),
121                ContractCostType::Bls12381G1CheckPointInSubgroup => (),
122                ContractCostType::Bls12381G2CheckPointOnCurve => (),
123                ContractCostType::Bls12381G2CheckPointInSubgroup => (),
124                ContractCostType::Bls12381G1ProjectiveToAffine => (),
125                ContractCostType::Bls12381G2ProjectiveToAffine => (),
126                ContractCostType::Bls12381G1Add => (),
127                ContractCostType::Bls12381G1Mul => (),
128                ContractCostType::Bls12381G1Msm => init_input(), // input is number of (G1,Fr) pairs
129                ContractCostType::Bls12381MapFpToG1 => (),
130                ContractCostType::Bls12381HashToG1 => init_input(),
131                ContractCostType::Bls12381G2Add => (),
132                ContractCostType::Bls12381G2Mul => (),
133                ContractCostType::Bls12381G2Msm => init_input(), // input is number of (G2,Fr) pairs
134                ContractCostType::Bls12381MapFp2ToG2 => (),
135                ContractCostType::Bls12381HashToG2 => init_input(),
136                ContractCostType::Bls12381Pairing => init_input(), // input is number of (G1,G2) pairs
137                ContractCostType::Bls12381FrFromU256 => (),
138                ContractCostType::Bls12381FrToU256 => (),
139                ContractCostType::Bls12381FrAddSub => (),
140                ContractCostType::Bls12381FrMul => (),
141                ContractCostType::Bls12381FrPow => init_input(), // input is number of bits in the u64 exponent excluding leading zeros
142                ContractCostType::Bls12381FrInv => (),
143            }
144        }
145        mt
146    }
147}
148
149impl BudgetTracker {
150    #[cfg(any(test, feature = "testutils", feature = "bench"))]
151    fn reset(&mut self) {
152        self.meter_count = 0;
153        for tracker in &mut self.cost_trackers {
154            tracker.iterations = 0;
155            tracker.inputs = tracker.inputs.map(|_| 0);
156            tracker.cpu = 0;
157            tracker.mem = 0;
158        }
159        self.wasm_memory = 0;
160    }
161
162    fn track_time(&mut self, ty: ContractCostType, duration: u64) -> Result<(), HostError> {
163        let t = self.time_tracker.get_mut(ty as usize).ok_or_else(|| {
164            HostError::from(Error::from_type_and_code(
165                ScErrorType::Budget,
166                ScErrorCode::InternalError,
167            ))
168        })?;
169        *t += duration;
170        Ok(())
171    }
172
173    fn get_time(&self, ty: ContractCostType) -> Result<u64, HostError> {
174        self.time_tracker
175            .get(ty as usize)
176            .map(|t| *t)
177            .ok_or_else(|| (ScErrorType::Budget, ScErrorCode::InternalError).into())
178    }
179}
180
181#[derive(Clone)]
182pub(crate) struct BudgetImpl {
183    cpu_insns: BudgetDimension,
184    mem_bytes: BudgetDimension,
185    /// For the purpose of calibration and reporting; not used for budget-limiting nor does it affect consensus
186    tracker: BudgetTracker,
187    is_in_shadow_mode: bool,
188    fuel_costs: wasmi::FuelCosts,
189    depth_limit: u32,
190}
191
192impl BudgetImpl {
193    /// Initializes the budget from network configuration settings.
194    fn try_from_configs(
195        cpu_limit: u64,
196        mem_limit: u64,
197        cpu_cost_params: ContractCostParams,
198        mem_cost_params: ContractCostParams,
199    ) -> Result<Self, HostError> {
200        Ok(Self {
201            cpu_insns: BudgetDimension::try_from_config(cpu_cost_params, cpu_limit)?,
202            mem_bytes: BudgetDimension::try_from_config(mem_cost_params, mem_limit)?,
203            tracker: BudgetTracker::default(),
204            is_in_shadow_mode: false,
205            fuel_costs: load_calibrated_fuel_costs(),
206            depth_limit: DEFAULT_HOST_DEPTH_LIMIT,
207        })
208    }
209
210    pub fn charge(
211        &mut self,
212        ty: ContractCostType,
213        iterations: u64,
214        input: Option<u64>,
215    ) -> Result<(), HostError> {
216        let tracker = self
217            .tracker
218            .cost_trackers
219            .get_mut(ty as usize)
220            .ok_or_else(|| HostError::from((ScErrorType::Budget, ScErrorCode::InternalError)))?;
221
222        if !self.is_in_shadow_mode {
223            // update tracker for reporting
224            self.tracker.meter_count = self.tracker.meter_count.saturating_add(1);
225            tracker.iterations = tracker.iterations.saturating_add(iterations);
226            match (&mut tracker.inputs, input) {
227                (None, None) => (),
228                (Some(t), Some(i)) => *t = t.saturating_add(i.saturating_mul(iterations)),
229                // internal logic error, a wrong cost type has been passed in
230                _ => return Err((ScErrorType::Budget, ScErrorCode::InternalError).into()),
231            };
232        }
233
234        let cpu_charged = self.cpu_insns.charge(
235            ty,
236            iterations,
237            input,
238            IsCpu(true),
239            IsShadowMode(self.is_in_shadow_mode),
240        )?;
241        if !self.is_in_shadow_mode {
242            tracker.cpu = tracker.cpu.saturating_add(cpu_charged);
243        }
244        self.cpu_insns
245            .check_budget_limit(IsShadowMode(self.is_in_shadow_mode))?;
246
247        let mem_charged = self.mem_bytes.charge(
248            ty,
249            iterations,
250            input,
251            IsCpu(false),
252            IsShadowMode(self.is_in_shadow_mode),
253        )?;
254        if !self.is_in_shadow_mode {
255            tracker.mem = tracker.mem.saturating_add(mem_charged);
256        }
257        self.mem_bytes
258            .check_budget_limit(IsShadowMode(self.is_in_shadow_mode))
259    }
260
261    fn get_wasmi_fuel_remaining(&self) -> Result<u64, HostError> {
262        let cpu_remaining = self.cpu_insns.get_remaining();
263        let cost_model = self
264            .cpu_insns
265            .get_cost_model(ContractCostType::WasmInsnExec)?;
266        let cpu_per_fuel = cost_model.const_term.max(1);
267        // Due to rounding, the amount of cpu converted to fuel will be slightly
268        // less than the total cpu available. This is okay because 1. that rounded-off
269        // amount should be very small (less than the cpu_per_fuel) 2. it does
270        // not cumulate over host function calls (each time the Vm returns back
271        // to the host, the host gets back the unspent fuel amount converged
272        // back to the cpu). The only way this rounding difference is observable
273        // is if the Vm traps due to `OutOfFuel`, this tiny amount would still
274        // be withheld from the host. And this may not be the only source of
275        // unspendable residual budget (see the other comment in `vm::wrapped_func_call`).
276        // So it should be okay.
277        Ok(cpu_remaining.checked_div(cpu_per_fuel).unwrap_or(0))
278    }
279}
280
281/// Default settings for local/sandbox testing only. The actual operations will use parameters
282/// read on-chain from network configuration via [`from_configs`] above.
283impl Default for BudgetImpl {
284    fn default() -> Self {
285        let mut b = Self {
286            cpu_insns: BudgetDimension::default(),
287            mem_bytes: BudgetDimension::default(),
288            tracker: Default::default(),
289            is_in_shadow_mode: false,
290            fuel_costs: load_calibrated_fuel_costs(),
291            depth_limit: DEFAULT_HOST_DEPTH_LIMIT,
292        };
293
294        for ct in ContractCostType::variants() {
295            // define the cpu cost model parameters
296            let Ok(cpu) = b.cpu_insns.get_cost_model_mut(ct) else {
297                continue;
298            };
299            match ct {
300                // This is the host cpu insn cost per wasm "fuel". Every "base" wasm
301                // instruction costs 1 fuel (by default), and some particular types of
302                // instructions may cost additional amount of fuel based on
303                // wasmi's config setting.
304                ContractCostType::WasmInsnExec => {
305                    cpu.const_term = 4;
306                    cpu.lin_term = ScaledU64(0);
307                }
308                // We don't have a clear way of modeling the linear term of
309                // memalloc cost thus we choose a reasonable upperbound which is
310                // same as other mem ops.
311                ContractCostType::MemAlloc => {
312                    cpu.const_term = 434;
313                    cpu.lin_term = ScaledU64::from_unscaled_u64(1).safe_div(8);
314                }
315                // We don't use a calibrated number for this because sending a
316                // large calibration-buffer to memcpy hits an optimized
317                // large-memcpy path in the stdlib, which has both a large
318                // overhead and a small per-byte cost. But large buffers aren't
319                // really how byte-copies usually get used in metered code. Most
320                // calls have to do with small copies of a few tens or hundreds
321                // of bytes. So instead we just "reason it out": we can probably
322                // copy 8 bytes per instruction on a 64-bit machine, and that
323                // therefore a 1-byte copy is considered 1/8th of an
324                // instruction. We also add in a nonzero constant overhead, to
325                // avoid having anything that can be zero cost and approximate
326                // whatever function call, arg-shuffling, spills, reloads or
327                // other flotsam accumulates around a typical memory copy.
328                ContractCostType::MemCpy => {
329                    cpu.const_term = 42;
330                    cpu.lin_term = ScaledU64::from_unscaled_u64(1).safe_div(8);
331                }
332                ContractCostType::MemCmp => {
333                    cpu.const_term = 44;
334                    cpu.lin_term = ScaledU64::from_unscaled_u64(1).safe_div(8);
335                }
336                ContractCostType::DispatchHostFunction => {
337                    cpu.const_term = 310;
338                    cpu.lin_term = ScaledU64(0);
339                }
340                ContractCostType::VisitObject => {
341                    cpu.const_term = 61;
342                    cpu.lin_term = ScaledU64(0);
343                }
344                ContractCostType::ValSer => {
345                    cpu.const_term = 230;
346                    cpu.lin_term = ScaledU64(29);
347                }
348                ContractCostType::ValDeser => {
349                    cpu.const_term = 59052;
350                    cpu.lin_term = ScaledU64(4001);
351                }
352                ContractCostType::ComputeSha256Hash => {
353                    cpu.const_term = 3738;
354                    cpu.lin_term = ScaledU64(7012);
355                }
356                ContractCostType::ComputeEd25519PubKey => {
357                    cpu.const_term = 40253;
358                    cpu.lin_term = ScaledU64(0);
359                }
360                ContractCostType::VerifyEd25519Sig => {
361                    cpu.const_term = 377524;
362                    cpu.lin_term = ScaledU64(4068);
363                }
364                ContractCostType::VmInstantiation => {
365                    cpu.const_term = 451626;
366                    cpu.lin_term = ScaledU64(45405);
367                }
368                ContractCostType::VmCachedInstantiation => {
369                    cpu.const_term = 41142;
370                    cpu.lin_term = ScaledU64(634);
371                }
372                ContractCostType::InvokeVmFunction => {
373                    cpu.const_term = 1948;
374                    cpu.lin_term = ScaledU64(0);
375                }
376                ContractCostType::ComputeKeccak256Hash => {
377                    cpu.const_term = 3766;
378                    cpu.lin_term = ScaledU64(5969);
379                }
380                ContractCostType::DecodeEcdsaCurve256Sig => {
381                    cpu.const_term = 710;
382                    cpu.lin_term = ScaledU64(0);
383                }
384                ContractCostType::RecoverEcdsaSecp256k1Key => {
385                    cpu.const_term = 2315295;
386                    cpu.lin_term = ScaledU64(0);
387                }
388                ContractCostType::Int256AddSub => {
389                    cpu.const_term = 4404;
390                    cpu.lin_term = ScaledU64(0);
391                }
392                ContractCostType::Int256Mul => {
393                    cpu.const_term = 4947;
394                    cpu.lin_term = ScaledU64(0);
395                }
396                ContractCostType::Int256Div => {
397                    cpu.const_term = 4911;
398                    cpu.lin_term = ScaledU64(0);
399                }
400                ContractCostType::Int256Pow => {
401                    cpu.const_term = 4286;
402                    cpu.lin_term = ScaledU64(0);
403                }
404                ContractCostType::Int256Shift => {
405                    cpu.const_term = 913;
406                    cpu.lin_term = ScaledU64(0);
407                }
408                ContractCostType::ChaCha20DrawBytes => {
409                    cpu.const_term = 1058;
410                    cpu.lin_term = ScaledU64(501);
411                }
412
413                ContractCostType::ParseWasmInstructions => {
414                    cpu.const_term = 73077;
415                    cpu.lin_term = ScaledU64(25410);
416                }
417                ContractCostType::ParseWasmFunctions => {
418                    cpu.const_term = 0;
419                    cpu.lin_term = ScaledU64(540752);
420                }
421                ContractCostType::ParseWasmGlobals => {
422                    cpu.const_term = 0;
423                    cpu.lin_term = ScaledU64(176363);
424                }
425                ContractCostType::ParseWasmTableEntries => {
426                    cpu.const_term = 0;
427                    cpu.lin_term = ScaledU64(29989);
428                }
429                ContractCostType::ParseWasmTypes => {
430                    cpu.const_term = 0;
431                    cpu.lin_term = ScaledU64(1061449);
432                }
433                ContractCostType::ParseWasmDataSegments => {
434                    cpu.const_term = 0;
435                    cpu.lin_term = ScaledU64(237336);
436                }
437                ContractCostType::ParseWasmElemSegments => {
438                    cpu.const_term = 0;
439                    cpu.lin_term = ScaledU64(328476);
440                }
441                ContractCostType::ParseWasmImports => {
442                    cpu.const_term = 0;
443                    cpu.lin_term = ScaledU64(701845);
444                }
445                ContractCostType::ParseWasmExports => {
446                    cpu.const_term = 0;
447                    cpu.lin_term = ScaledU64(429383);
448                }
449                ContractCostType::ParseWasmDataSegmentBytes => {
450                    cpu.const_term = 0;
451                    cpu.lin_term = ScaledU64(28);
452                }
453                ContractCostType::InstantiateWasmInstructions => {
454                    cpu.const_term = 43030;
455                    cpu.lin_term = ScaledU64(0);
456                }
457                ContractCostType::InstantiateWasmFunctions => {
458                    cpu.const_term = 0;
459                    cpu.lin_term = ScaledU64(7556);
460                }
461                ContractCostType::InstantiateWasmGlobals => {
462                    cpu.const_term = 0;
463                    cpu.lin_term = ScaledU64(10711);
464                }
465                ContractCostType::InstantiateWasmTableEntries => {
466                    cpu.const_term = 0;
467                    cpu.lin_term = ScaledU64(3300);
468                }
469                ContractCostType::InstantiateWasmTypes => {
470                    cpu.const_term = 0;
471                    cpu.lin_term = ScaledU64(0);
472                }
473                ContractCostType::InstantiateWasmDataSegments => {
474                    cpu.const_term = 0;
475                    cpu.lin_term = ScaledU64(23038);
476                }
477                ContractCostType::InstantiateWasmElemSegments => {
478                    cpu.const_term = 0;
479                    cpu.lin_term = ScaledU64(42488);
480                }
481                ContractCostType::InstantiateWasmImports => {
482                    cpu.const_term = 0;
483                    cpu.lin_term = ScaledU64(828974);
484                }
485                ContractCostType::InstantiateWasmExports => {
486                    cpu.const_term = 0;
487                    cpu.lin_term = ScaledU64(297100);
488                }
489                ContractCostType::InstantiateWasmDataSegmentBytes => {
490                    cpu.const_term = 0;
491                    cpu.lin_term = ScaledU64(14);
492                }
493                ContractCostType::Sec1DecodePointUncompressed => {
494                    cpu.const_term = 1882;
495                    cpu.lin_term = ScaledU64(0);
496                }
497                ContractCostType::VerifyEcdsaSecp256r1Sig => {
498                    cpu.const_term = 3000906;
499                    cpu.lin_term = ScaledU64(0);
500                }
501                ContractCostType::Bls12381EncodeFp => {
502                    cpu.const_term = 661;
503                    cpu.lin_term = ScaledU64(0);
504                }
505                ContractCostType::Bls12381DecodeFp => {
506                    cpu.const_term = 985;
507                    cpu.lin_term = ScaledU64(0);
508                }
509                ContractCostType::Bls12381G1CheckPointOnCurve => {
510                    cpu.const_term = 1934;
511                    cpu.lin_term = ScaledU64(0);
512                }
513                ContractCostType::Bls12381G1CheckPointInSubgroup => {
514                    cpu.const_term = 730510;
515                    cpu.lin_term = ScaledU64(0);
516                }
517                ContractCostType::Bls12381G2CheckPointOnCurve => {
518                    cpu.const_term = 5921;
519                    cpu.lin_term = ScaledU64(0);
520                }
521                ContractCostType::Bls12381G2CheckPointInSubgroup => {
522                    cpu.const_term = 1057822;
523                    cpu.lin_term = ScaledU64(0);
524                }
525                ContractCostType::Bls12381G1ProjectiveToAffine => {
526                    cpu.const_term = 92642;
527                    cpu.lin_term = ScaledU64(0);
528                }
529                ContractCostType::Bls12381G2ProjectiveToAffine => {
530                    cpu.const_term = 100742;
531                    cpu.lin_term = ScaledU64(0);
532                }
533                ContractCostType::Bls12381G1Add => {
534                    cpu.const_term = 7689;
535                    cpu.lin_term = ScaledU64(0);
536                }
537                ContractCostType::Bls12381G1Mul => {
538                    cpu.const_term = 2458985;
539                    cpu.lin_term = ScaledU64(0);
540                }
541                ContractCostType::Bls12381G1Msm => {
542                    cpu.const_term = 2426722;
543                    cpu.lin_term = ScaledU64(96397671);
544                }
545                ContractCostType::Bls12381MapFpToG1 => {
546                    cpu.const_term = 1541554;
547                    cpu.lin_term = ScaledU64(0);
548                }
549                ContractCostType::Bls12381HashToG1 => {
550                    cpu.const_term = 3211191;
551                    cpu.lin_term = ScaledU64(6713);
552                }
553                ContractCostType::Bls12381G2Add => {
554                    cpu.const_term = 25207;
555                    cpu.lin_term = ScaledU64(0);
556                }
557                ContractCostType::Bls12381G2Mul => {
558                    cpu.const_term = 7873219;
559                    cpu.lin_term = ScaledU64(0);
560                }
561                ContractCostType::Bls12381G2Msm => {
562                    cpu.const_term = 8035968;
563                    cpu.lin_term = ScaledU64(309667335);
564                }
565                ContractCostType::Bls12381MapFp2ToG2 => {
566                    cpu.const_term = 2420202;
567                    cpu.lin_term = ScaledU64(0);
568                }
569                ContractCostType::Bls12381HashToG2 => {
570                    cpu.const_term = 7050564;
571                    cpu.lin_term = ScaledU64(6797);
572                }
573                ContractCostType::Bls12381Pairing => {
574                    cpu.const_term = 10558948;
575                    cpu.lin_term = ScaledU64(632860943);
576                }
577                ContractCostType::Bls12381FrFromU256 => {
578                    cpu.const_term = 1994;
579                    cpu.lin_term = ScaledU64(0);
580                }
581                ContractCostType::Bls12381FrToU256 => {
582                    cpu.const_term = 1155;
583                    cpu.lin_term = ScaledU64(0);
584                }
585                ContractCostType::Bls12381FrAddSub => {
586                    cpu.const_term = 74;
587                    cpu.lin_term = ScaledU64(0);
588                }
589                ContractCostType::Bls12381FrMul => {
590                    cpu.const_term = 332;
591                    cpu.lin_term = ScaledU64(0);
592                }
593                ContractCostType::Bls12381FrPow => {
594                    cpu.const_term = 691;
595                    cpu.lin_term = ScaledU64(74558);
596                }
597                ContractCostType::Bls12381FrInv => {
598                    cpu.const_term = 35421;
599                    cpu.lin_term = ScaledU64(0);
600                }
601            }
602
603            // define the memory cost model parameters
604            let Ok(mem) = b.mem_bytes.get_cost_model_mut(ct) else {
605                continue;
606            };
607            match ct {
608                // This type is designated to the cpu cost. By definition, the memory cost
609                // of a (cpu) fuel is zero.
610                ContractCostType::WasmInsnExec => {
611                    mem.const_term = 0;
612                    mem.lin_term = ScaledU64(0);
613                }
614                ContractCostType::MemAlloc => {
615                    mem.const_term = 16;
616                    mem.lin_term = ScaledU64::from_unscaled_u64(1);
617                }
618                ContractCostType::MemCpy => {
619                    mem.const_term = 0;
620                    mem.lin_term = ScaledU64(0);
621                }
622                ContractCostType::MemCmp => {
623                    mem.const_term = 0;
624                    mem.lin_term = ScaledU64(0);
625                }
626                ContractCostType::DispatchHostFunction => {
627                    mem.const_term = 0;
628                    mem.lin_term = ScaledU64(0);
629                }
630                ContractCostType::VisitObject => {
631                    mem.const_term = 0;
632                    mem.lin_term = ScaledU64(0);
633                }
634                // These are derived analytically but based on calibration on
635                // highly nested xdr structures
636                ContractCostType::ValSer => {
637                    mem.const_term = 242;
638                    mem.lin_term = ScaledU64::from_unscaled_u64(3);
639                }
640                ContractCostType::ValDeser => {
641                    mem.const_term = 0;
642                    mem.lin_term = ScaledU64::from_unscaled_u64(3);
643                }
644                ContractCostType::ComputeSha256Hash => {
645                    mem.const_term = 0;
646                    mem.lin_term = ScaledU64(0);
647                }
648                ContractCostType::ComputeEd25519PubKey => {
649                    mem.const_term = 0;
650                    mem.lin_term = ScaledU64(0);
651                }
652                ContractCostType::VerifyEd25519Sig => {
653                    mem.const_term = 0;
654                    mem.lin_term = ScaledU64(0);
655                }
656                ContractCostType::VmInstantiation => {
657                    mem.const_term = 130065;
658                    mem.lin_term = ScaledU64(5064);
659                }
660                ContractCostType::VmCachedInstantiation => {
661                    mem.const_term = 69472;
662                    mem.lin_term = ScaledU64(1217);
663                }
664                ContractCostType::InvokeVmFunction => {
665                    mem.const_term = 14;
666                    mem.lin_term = ScaledU64(0);
667                }
668                ContractCostType::ComputeKeccak256Hash => {
669                    mem.const_term = 0;
670                    mem.lin_term = ScaledU64(0);
671                }
672                ContractCostType::DecodeEcdsaCurve256Sig => {
673                    mem.const_term = 0;
674                    mem.lin_term = ScaledU64(0);
675                }
676                ContractCostType::RecoverEcdsaSecp256k1Key => {
677                    mem.const_term = 181;
678                    mem.lin_term = ScaledU64(0);
679                }
680                ContractCostType::Int256AddSub => {
681                    mem.const_term = 99;
682                    mem.lin_term = ScaledU64(0);
683                }
684                ContractCostType::Int256Mul => {
685                    mem.const_term = 99;
686                    mem.lin_term = ScaledU64(0);
687                }
688                ContractCostType::Int256Div => {
689                    mem.const_term = 99;
690                    mem.lin_term = ScaledU64(0);
691                }
692                ContractCostType::Int256Pow => {
693                    mem.const_term = 99;
694                    mem.lin_term = ScaledU64(0);
695                }
696                ContractCostType::Int256Shift => {
697                    mem.const_term = 99;
698                    mem.lin_term = ScaledU64(0);
699                }
700                ContractCostType::ChaCha20DrawBytes => {
701                    mem.const_term = 0;
702                    mem.lin_term = ScaledU64(0);
703                }
704
705                ContractCostType::ParseWasmInstructions => {
706                    mem.const_term = 17564;
707                    mem.lin_term = ScaledU64(6457);
708                }
709                ContractCostType::ParseWasmFunctions => {
710                    mem.const_term = 0;
711                    mem.lin_term = ScaledU64(47464);
712                }
713                ContractCostType::ParseWasmGlobals => {
714                    mem.const_term = 0;
715                    mem.lin_term = ScaledU64(13420);
716                }
717                ContractCostType::ParseWasmTableEntries => {
718                    mem.const_term = 0;
719                    mem.lin_term = ScaledU64(6285);
720                }
721                ContractCostType::ParseWasmTypes => {
722                    mem.const_term = 0;
723                    mem.lin_term = ScaledU64(64670);
724                }
725                ContractCostType::ParseWasmDataSegments => {
726                    mem.const_term = 0;
727                    mem.lin_term = ScaledU64(29074);
728                }
729                ContractCostType::ParseWasmElemSegments => {
730                    mem.const_term = 0;
731                    mem.lin_term = ScaledU64(48095);
732                }
733                ContractCostType::ParseWasmImports => {
734                    mem.const_term = 0;
735                    mem.lin_term = ScaledU64(103229);
736                }
737                ContractCostType::ParseWasmExports => {
738                    mem.const_term = 0;
739                    mem.lin_term = ScaledU64(36394);
740                }
741                ContractCostType::ParseWasmDataSegmentBytes => {
742                    mem.const_term = 0;
743                    mem.lin_term = ScaledU64(257);
744                }
745                ContractCostType::InstantiateWasmInstructions => {
746                    mem.const_term = 70704;
747                    mem.lin_term = ScaledU64(0);
748                }
749                ContractCostType::InstantiateWasmFunctions => {
750                    mem.const_term = 0;
751                    mem.lin_term = ScaledU64(14613);
752                }
753                ContractCostType::InstantiateWasmGlobals => {
754                    mem.const_term = 0;
755                    mem.lin_term = ScaledU64(6833);
756                }
757                ContractCostType::InstantiateWasmTableEntries => {
758                    mem.const_term = 0;
759                    mem.lin_term = ScaledU64(1025);
760                }
761                ContractCostType::InstantiateWasmTypes => {
762                    mem.const_term = 0;
763                    mem.lin_term = ScaledU64(0);
764                }
765                ContractCostType::InstantiateWasmDataSegments => {
766                    mem.const_term = 0;
767                    mem.lin_term = ScaledU64(129632);
768                }
769                ContractCostType::InstantiateWasmElemSegments => {
770                    mem.const_term = 0;
771                    mem.lin_term = ScaledU64(13665);
772                }
773                ContractCostType::InstantiateWasmImports => {
774                    mem.const_term = 0;
775                    mem.lin_term = ScaledU64(97637);
776                }
777                ContractCostType::InstantiateWasmExports => {
778                    mem.const_term = 0;
779                    mem.lin_term = ScaledU64(9176);
780                }
781                ContractCostType::InstantiateWasmDataSegmentBytes => {
782                    mem.const_term = 0;
783                    mem.lin_term = ScaledU64(126);
784                }
785                ContractCostType::Sec1DecodePointUncompressed => {
786                    mem.const_term = 0;
787                    mem.lin_term = ScaledU64(0);
788                }
789                ContractCostType::VerifyEcdsaSecp256r1Sig => {
790                    mem.const_term = 0;
791                    mem.lin_term = ScaledU64(0);
792                }
793                ContractCostType::Bls12381EncodeFp => {
794                    mem.const_term = 0;
795                    mem.lin_term = ScaledU64(0);
796                }
797                ContractCostType::Bls12381DecodeFp => {
798                    mem.const_term = 0;
799                    mem.lin_term = ScaledU64(0);
800                }
801                ContractCostType::Bls12381G1CheckPointOnCurve => {
802                    mem.const_term = 0;
803                    mem.lin_term = ScaledU64(0);
804                }
805                ContractCostType::Bls12381G1CheckPointInSubgroup => {
806                    mem.const_term = 0;
807                    mem.lin_term = ScaledU64(0);
808                }
809                ContractCostType::Bls12381G2CheckPointOnCurve => {
810                    mem.const_term = 0;
811                    mem.lin_term = ScaledU64(0);
812                }
813                ContractCostType::Bls12381G2CheckPointInSubgroup => {
814                    mem.const_term = 0;
815                    mem.lin_term = ScaledU64(0);
816                }
817                ContractCostType::Bls12381G1ProjectiveToAffine => {
818                    mem.const_term = 0;
819                    mem.lin_term = ScaledU64(0);
820                }
821                ContractCostType::Bls12381G2ProjectiveToAffine => {
822                    mem.const_term = 0;
823                    mem.lin_term = ScaledU64(0);
824                }
825                ContractCostType::Bls12381G1Add => {
826                    mem.const_term = 0;
827                    mem.lin_term = ScaledU64(0);
828                }
829                ContractCostType::Bls12381G1Mul => {
830                    mem.const_term = 0;
831                    mem.lin_term = ScaledU64(0);
832                }
833                ContractCostType::Bls12381G1Msm => {
834                    mem.const_term = 109494;
835                    mem.lin_term = ScaledU64(354667);
836                }
837                ContractCostType::Bls12381MapFpToG1 => {
838                    mem.const_term = 5552;
839                    mem.lin_term = ScaledU64(0);
840                }
841                ContractCostType::Bls12381HashToG1 => {
842                    mem.const_term = 9424;
843                    mem.lin_term = ScaledU64(0);
844                }
845                ContractCostType::Bls12381G2Add => {
846                    mem.const_term = 0;
847                    mem.lin_term = ScaledU64(0);
848                }
849                ContractCostType::Bls12381G2Mul => {
850                    mem.const_term = 0;
851                    mem.lin_term = ScaledU64(0);
852                }
853                ContractCostType::Bls12381G2Msm => {
854                    mem.const_term = 219654;
855                    mem.lin_term = ScaledU64(354667);
856                }
857                ContractCostType::Bls12381MapFp2ToG2 => {
858                    mem.const_term = 3344;
859                    mem.lin_term = ScaledU64(0);
860                }
861                ContractCostType::Bls12381HashToG2 => {
862                    mem.const_term = 6816;
863                    mem.lin_term = ScaledU64(0);
864                }
865                ContractCostType::Bls12381Pairing => {
866                    mem.const_term = 2204;
867                    mem.lin_term = ScaledU64(9340474);
868                }
869                ContractCostType::Bls12381FrFromU256 => {
870                    mem.const_term = 0;
871                    mem.lin_term = ScaledU64(0);
872                }
873                ContractCostType::Bls12381FrToU256 => {
874                    mem.const_term = 248;
875                    mem.lin_term = ScaledU64(0);
876                }
877                ContractCostType::Bls12381FrAddSub => {
878                    mem.const_term = 0;
879                    mem.lin_term = ScaledU64(0);
880                }
881                ContractCostType::Bls12381FrMul => {
882                    mem.const_term = 0;
883                    mem.lin_term = ScaledU64(0);
884                }
885                ContractCostType::Bls12381FrPow => {
886                    mem.const_term = 0;
887                    mem.lin_term = ScaledU64(128);
888                }
889                ContractCostType::Bls12381FrInv => {
890                    mem.const_term = 0;
891                    mem.lin_term = ScaledU64(0);
892                }
893            }
894        }
895
896        // define the limits
897        b.cpu_insns.reset(limits::DEFAULT_CPU_INSN_LIMIT);
898        b.mem_bytes.reset(limits::DEFAULT_MEM_BYTES_LIMIT);
899        b
900    }
901}
902
903impl Debug for BudgetImpl {
904    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
905        writeln!(f, "{:=<175}", "")?;
906        writeln!(
907            f,
908            "Cpu limit: {}; used: {}",
909            self.cpu_insns.limit, self.cpu_insns.total_count
910        )?;
911        writeln!(
912            f,
913            "Mem limit: {}; used: {}",
914            self.mem_bytes.limit, self.mem_bytes.total_count
915        )?;
916        writeln!(f, "{:=<175}", "")?;
917        writeln!(
918            f,
919            "{:<35}{:<15}{:<15}{:<15}{:<15}{:<20}{:<20}{:<20}{:<20}",
920            "CostType",
921            "iterations",
922            "input",
923            "cpu_insns",
924            "mem_bytes",
925            "const_term_cpu",
926            "lin_term_cpu",
927            "const_term_mem",
928            "lin_term_mem",
929        )?;
930        for ct in ContractCostType::variants() {
931            let i = ct as usize;
932            writeln!(
933                f,
934                "{:<35}{:<15}{:<15}{:<15}{:<15}{:<20}{:<20}{:<20}{:<20}",
935                format!("{:?}", ct),
936                self.tracker.cost_trackers[i].iterations,
937                format!("{:?}", self.tracker.cost_trackers[i].inputs),
938                self.tracker.cost_trackers[i].cpu,
939                self.tracker.cost_trackers[i].mem,
940                self.cpu_insns.cost_models[i].const_term,
941                format!("{}", self.cpu_insns.cost_models[i].lin_term),
942                self.mem_bytes.cost_models[i].const_term,
943                format!("{}", self.mem_bytes.cost_models[i].lin_term),
944            )?;
945        }
946        writeln!(f, "{:=<175}", "")?;
947        writeln!(
948            f,
949            "Internal details (diagnostics info, does not affect fees) "
950        )?;
951        writeln!(
952            f,
953            "Total # times meter was called: {}",
954            self.tracker.meter_count,
955        )?;
956        writeln!(
957            f,
958            "Shadow cpu limit: {}; used: {}",
959            self.cpu_insns.shadow_limit, self.cpu_insns.shadow_total_count
960        )?;
961        writeln!(
962            f,
963            "Shadow mem limit: {}; used: {}",
964            self.mem_bytes.shadow_limit, self.mem_bytes.shadow_total_count
965        )?;
966        writeln!(f, "{:=<175}", "")?;
967        Ok(())
968    }
969}
970
971impl Display for BudgetImpl {
972    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
973        writeln!(f, "{:=<65}", "")?;
974        writeln!(
975            f,
976            "Cpu limit: {}; used: {}",
977            self.cpu_insns.limit, self.cpu_insns.total_count
978        )?;
979        writeln!(
980            f,
981            "Mem limit: {}; used: {}",
982            self.mem_bytes.limit, self.mem_bytes.total_count
983        )?;
984        writeln!(f, "{:=<65}", "")?;
985        writeln!(
986            f,
987            "{:<35}{:<15}{:<15}",
988            "CostType", "cpu_insns", "mem_bytes",
989        )?;
990        for ct in ContractCostType::variants() {
991            let i = ct as usize;
992            writeln!(
993                f,
994                "{:<35}{:<15}{:<15}",
995                format!("{:?}", ct),
996                self.tracker.cost_trackers[i].cpu,
997                self.tracker.cost_trackers[i].mem,
998            )?;
999        }
1000        writeln!(f, "{:=<65}", "")?;
1001        Ok(())
1002    }
1003}
1004
1005#[allow(unused)]
1006#[cfg(test)]
1007impl BudgetImpl {
1008    // Utility function for printing default budget cost parameters in cpp format
1009    // so that it can be ported into stellar-core.
1010    // When needing it, copy and run the following test
1011    // ```
1012    // #[test]
1013    // fn test() {
1014    //     let bi = BudgetImpl::default();
1015    //     bi.print_default_params_in_cpp();
1016    // }
1017    // ```
1018    // and copy the screen output.
1019    fn print_default_params_in_cpp(&self) {
1020        // cpu
1021        println!();
1022        println!();
1023        println!();
1024        for ct in ContractCostType::variants() {
1025            let Ok(cpu) = self.cpu_insns.get_cost_model(ct) else {
1026                continue;
1027            };
1028            println!("case {}:", ct.name());
1029            println!(
1030                "params[val] = ContractCostParamEntry{{ExtensionPoint{{0}}, {}, {}}};",
1031                cpu.const_term, cpu.lin_term.0
1032            );
1033            println!("break;");
1034        }
1035        // mem
1036        println!();
1037        println!();
1038        println!();
1039        for ct in ContractCostType::variants() {
1040            let Ok(mem) = self.mem_bytes.get_cost_model(ct) else {
1041                continue;
1042            };
1043            println!("case {}:", ct.name());
1044            println!(
1045                "params[val] = ContractCostParamEntry{{ExtensionPoint{{0}}, {}, {}}};",
1046                mem.const_term, mem.lin_term.0
1047            );
1048            println!("break;");
1049        }
1050        println!();
1051        println!();
1052        println!();
1053    }
1054}
1055
1056#[derive(Clone)]
1057pub struct Budget(pub(crate) Rc<RefCell<BudgetImpl>>);
1058
1059#[allow(clippy::derivable_impls)]
1060impl Default for Budget {
1061    fn default() -> Self {
1062        #[cfg(all(not(target_family = "wasm"), feature = "tracy"))]
1063        let _client = tracy_client::Client::start();
1064        Self(Default::default())
1065    }
1066}
1067
1068impl Debug for Budget {
1069    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1070        writeln!(f, "{:?}", self.0.try_borrow().map_err(|_| std::fmt::Error)?)
1071    }
1072}
1073
1074impl Display for Budget {
1075    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1076        writeln!(f, "{}", self.0.try_borrow().map_err(|_| std::fmt::Error)?)
1077    }
1078}
1079
1080pub trait AsBudget: Clone {
1081    fn as_budget(&self) -> &Budget;
1082}
1083
1084impl AsBudget for Budget {
1085    fn as_budget(&self) -> &Budget {
1086        self
1087    }
1088}
1089
1090impl AsBudget for &Budget {
1091    fn as_budget(&self) -> &Budget {
1092        self
1093    }
1094}
1095
1096impl AsBudget for Host {
1097    fn as_budget(&self) -> &Budget {
1098        self.budget_ref()
1099    }
1100}
1101
1102impl AsBudget for &Host {
1103    fn as_budget(&self) -> &Budget {
1104        self.budget_ref()
1105    }
1106}
1107
1108impl Budget {
1109    /// Initializes the budget from network configuration settings.
1110    pub fn try_from_configs(
1111        cpu_limit: u64,
1112        mem_limit: u64,
1113        cpu_cost_params: ContractCostParams,
1114        mem_cost_params: ContractCostParams,
1115    ) -> Result<Self, HostError> {
1116        Ok(Self(Rc::new(RefCell::new(BudgetImpl::try_from_configs(
1117            cpu_limit,
1118            mem_limit,
1119            cpu_cost_params,
1120            mem_cost_params,
1121        )?))))
1122    }
1123
1124    // Helper function to avoid panics from multiple borrow_muts
1125    fn with_mut_budget<T, F>(&self, f: F) -> Result<T, HostError>
1126    where
1127        F: FnOnce(RefMut<BudgetImpl>) -> Result<T, HostError>,
1128    {
1129        f(self.0.try_borrow_mut_or_err()?)
1130    }
1131
1132    /// Performs a bulk charge to the budget under the specified [`CostType`].
1133    /// The `iterations` is the batch size. The caller needs to ensure:
1134    /// 1. the batched charges have identical costs (having the same
1135    /// [`CostType`] and `input`)
1136    /// 2. The input passed in (Some/None) is consistent with the [`CostModel`]
1137    /// underneath the [`CostType`] (linear/constant).
1138    pub fn bulk_charge(
1139        &self,
1140        ty: ContractCostType,
1141        iterations: u64,
1142        input: Option<u64>,
1143    ) -> Result<(), HostError> {
1144        self.0
1145            .try_borrow_mut_or_err()?
1146            .charge(ty, iterations, input)
1147    }
1148
1149    /// Charges the budget under the specified [`CostType`]. The actual amount
1150    /// charged is determined by the underlying [`CostModel`] and may depend on
1151    /// the input. If the input is `None`, the model is assumed to be constant.
1152    /// Otherwise it is a linear model.  The caller needs to ensure the input
1153    /// passed is consistent with the inherent model underneath.
1154    pub fn charge(&self, ty: ContractCostType, input: Option<u64>) -> Result<(), HostError> {
1155        self.0.try_borrow_mut_or_err()?.charge(ty, 1, input)
1156    }
1157
1158    /// Runs a user provided closure in shadow mode -- all metering is done
1159    /// through the shadow budget.
1160    ///
1161    /// Because shadow mode is _designed not to be observed_ (indeed it exists
1162    /// primarily to count actions against the shadow budget that are _optional_
1163    /// on a given host, such as debug logging, and that therefore must strictly
1164    /// must not be observed), any error that occurs during execution is
1165    /// swallowed.
1166    pub(crate) fn with_shadow_mode<T, F>(&self, f: F)
1167    where
1168        F: FnOnce() -> Result<T, HostError>,
1169    {
1170        let mut prev = false;
1171
1172        if self
1173            .with_mut_budget(|mut b| {
1174                prev = b.is_in_shadow_mode;
1175                b.is_in_shadow_mode = true;
1176                b.cpu_insns.check_budget_limit(IsShadowMode(true))?;
1177                b.mem_bytes.check_budget_limit(IsShadowMode(true))
1178            })
1179            .is_ok()
1180        {
1181            let _ = f();
1182        }
1183
1184        let _ = self.with_mut_budget(|mut b| {
1185            b.is_in_shadow_mode = prev;
1186            Ok(())
1187        });
1188    }
1189
1190    pub(crate) fn is_in_shadow_mode(&self) -> Result<bool, HostError> {
1191        Ok(self.0.try_borrow_or_err()?.is_in_shadow_mode)
1192    }
1193
1194    pub(crate) fn set_shadow_limits(&self, cpu: u64, mem: u64) -> Result<(), HostError> {
1195        self.0.try_borrow_mut_or_err()?.cpu_insns.shadow_limit = cpu;
1196        self.0.try_borrow_mut_or_err()?.mem_bytes.shadow_limit = mem;
1197        Ok(())
1198    }
1199
1200    pub(crate) fn ensure_shadow_cpu_limit_factor(&self, factor: u64) -> Result<(), HostError> {
1201        let mut b = self.0.try_borrow_mut_or_err()?;
1202        b.cpu_insns.shadow_limit = b.cpu_insns.limit.saturating_mul(factor);
1203        Ok(())
1204    }
1205
1206    pub(crate) fn ensure_shadow_mem_limit_factor(&self, factor: u64) -> Result<(), HostError> {
1207        let mut b = self.0.try_borrow_mut_or_err()?;
1208        b.mem_bytes.shadow_limit = b.mem_bytes.limit.saturating_mul(factor);
1209        Ok(())
1210    }
1211
1212    pub fn get_tracker(&self, ty: ContractCostType) -> Result<CostTracker, HostError> {
1213        self.0
1214            .try_borrow_or_err()?
1215            .tracker
1216            .cost_trackers
1217            .get(ty as usize)
1218            .map(|x| *x)
1219            .ok_or_else(|| (ScErrorType::Budget, ScErrorCode::InternalError).into())
1220    }
1221
1222    pub fn get_time(&self, ty: ContractCostType) -> Result<u64, HostError> {
1223        self.0.try_borrow_or_err()?.tracker.get_time(ty)
1224    }
1225
1226    pub fn track_time(&self, ty: ContractCostType, duration: u64) -> Result<(), HostError> {
1227        self.0
1228            .try_borrow_mut_or_err()?
1229            .tracker
1230            .track_time(ty, duration)
1231    }
1232
1233    pub fn get_cpu_insns_consumed(&self) -> Result<u64, HostError> {
1234        Ok(self.0.try_borrow_or_err()?.cpu_insns.get_total_count())
1235    }
1236
1237    pub fn get_mem_bytes_consumed(&self) -> Result<u64, HostError> {
1238        Ok(self.0.try_borrow_or_err()?.mem_bytes.get_total_count())
1239    }
1240
1241    pub fn get_cpu_insns_remaining(&self) -> Result<u64, HostError> {
1242        Ok(self.0.try_borrow_or_err()?.cpu_insns.get_remaining())
1243    }
1244
1245    pub fn get_mem_bytes_remaining(&self) -> Result<u64, HostError> {
1246        Ok(self.0.try_borrow_or_err()?.mem_bytes.get_remaining())
1247    }
1248
1249    pub(crate) fn get_wasmi_fuel_remaining(&self) -> Result<u64, HostError> {
1250        self.0.try_borrow_mut_or_err()?.get_wasmi_fuel_remaining()
1251    }
1252
1253    pub fn reset_default(&self) -> Result<(), HostError> {
1254        *self.0.try_borrow_mut_or_err()? = BudgetImpl::default();
1255        Ok(())
1256    }
1257}
1258
1259#[test]
1260fn test_budget_initialization() -> Result<(), HostError> {
1261    use crate::xdr::{ContractCostParamEntry, ExtensionPoint};
1262    let cpu_cost_params = ContractCostParams(
1263        vec![
1264            ContractCostParamEntry {
1265                ext: ExtensionPoint::V0,
1266                const_term: 35,
1267                linear_term: 36,
1268            },
1269            ContractCostParamEntry {
1270                ext: ExtensionPoint::V0,
1271                const_term: 37,
1272                linear_term: 38,
1273            },
1274        ]
1275        .try_into()
1276        .unwrap(),
1277    );
1278    let mem_cost_params = ContractCostParams(
1279        vec![
1280            ContractCostParamEntry {
1281                ext: ExtensionPoint::V0,
1282                const_term: 39,
1283                linear_term: 40,
1284            },
1285            ContractCostParamEntry {
1286                ext: ExtensionPoint::V0,
1287                const_term: 41,
1288                linear_term: 42,
1289            },
1290            ContractCostParamEntry {
1291                ext: ExtensionPoint::V0,
1292                const_term: 43,
1293                linear_term: 44,
1294            },
1295        ]
1296        .try_into()
1297        .unwrap(),
1298    );
1299
1300    let budget = Budget::try_from_configs(100, 100, cpu_cost_params, mem_cost_params)?;
1301    assert_eq!(
1302        budget.0.try_borrow_or_err()?.cpu_insns.cost_models.len(),
1303        ContractCostType::variants().len()
1304    );
1305    assert_eq!(
1306        budget.0.try_borrow_or_err()?.mem_bytes.cost_models.len(),
1307        ContractCostType::variants().len()
1308    );
1309    assert_eq!(
1310        budget.0.try_borrow_or_err()?.tracker.cost_trackers.len(),
1311        ContractCostType::variants().len()
1312    );
1313    assert_eq!(
1314        budget.0.try_borrow_or_err()?.tracker.time_tracker.len(),
1315        ContractCostType::variants().len()
1316    );
1317
1318    Ok(())
1319}