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