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 cost_trackers: [CostTracker; ContractCostType::variants().len()],
38 meter_count: u32,
40 #[cfg(any(test, feature = "testutils", feature = "bench"))]
41 wasm_memory: u64,
42 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 let mut init_input = || tracker.inputs = Some(0);
64 match ct {
65 ContractCostType::WasmInsnExec => (),
66 ContractCostType::MemAlloc => init_input(), ContractCostType::MemCpy => init_input(), ContractCostType::MemCmp => init_input(), ContractCostType::DispatchHostFunction => (),
70 ContractCostType::VisitObject => (),
71 ContractCostType::ValSer => init_input(), ContractCostType::ValDeser => init_input(), ContractCostType::ComputeSha256Hash => init_input(), ContractCostType::ComputeEd25519PubKey => (),
82 ContractCostType::VerifyEd25519Sig => init_input(), ContractCostType::VmInstantiation => init_input(), ContractCostType::VmCachedInstantiation => init_input(), ContractCostType::InvokeVmFunction => (),
86 ContractCostType::ComputeKeccak256Hash => init_input(), 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(), 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(), ContractCostType::Bls12381MapFpToG1 => (),
130 ContractCostType::Bls12381HashToG1 => init_input(),
131 ContractCostType::Bls12381G2Add => (),
132 ContractCostType::Bls12381G2Mul => (),
133 ContractCostType::Bls12381G2Msm => init_input(), ContractCostType::Bls12381MapFp2ToG2 => (),
135 ContractCostType::Bls12381HashToG2 => init_input(),
136 ContractCostType::Bls12381Pairing => init_input(), ContractCostType::Bls12381FrFromU256 => (),
138 ContractCostType::Bls12381FrToU256 => (),
139 ContractCostType::Bls12381FrAddSub => (),
140 ContractCostType::Bls12381FrMul => (),
141 ContractCostType::Bls12381FrPow => init_input(), 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 tracker: BudgetTracker,
187 is_in_shadow_mode: bool,
188 fuel_costs: wasmi::FuelCosts,
189 depth_limit: u32,
190}
191
192impl BudgetImpl {
193 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 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 _ => 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 Ok(cpu_remaining.checked_div(cpu_per_fuel).unwrap_or(0))
287 }
288}
289
290impl 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 let Ok(cpu) = b.cpu_insns.get_cost_model_mut(ct) else {
306 continue;
307 };
308 match ct {
309 ContractCostType::WasmInsnExec => {
314 cpu.const_term = 4;
315 cpu.lin_term = ScaledU64(0);
316 }
317 ContractCostType::MemAlloc => {
321 cpu.const_term = 434;
322 cpu.lin_term = ScaledU64::from_unscaled_u64(1).safe_div(8);
323 }
324 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 let Ok(mem) = b.mem_bytes.get_cost_model_mut(ct) else {
614 continue;
615 };
616 match ct {
617 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 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 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 fn print_default_params_in_cpp(&self) {
1029 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 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 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 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 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 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 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 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}