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