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 fn charge(
211 &mut self,
212 ty: ContractCostType,
213 iterations: u64,
214 input: Option<u64>,
215 ) -> Result<(), HostError> {
216 let tracker = self
217 .tracker
218 .cost_trackers
219 .get_mut(ty as usize)
220 .ok_or_else(|| HostError::from((ScErrorType::Budget, ScErrorCode::InternalError)))?;
221
222 if !self.is_in_shadow_mode {
223 self.tracker.meter_count = self.tracker.meter_count.saturating_add(1);
225 tracker.iterations = tracker.iterations.saturating_add(iterations);
226 match (&mut tracker.inputs, input) {
227 (None, None) => (),
228 (Some(t), Some(i)) => *t = t.saturating_add(i.saturating_mul(iterations)),
229 _ => return Err((ScErrorType::Budget, ScErrorCode::InternalError).into()),
231 };
232 }
233
234 let cpu_charged = self.cpu_insns.charge(
235 ty,
236 iterations,
237 input,
238 IsCpu(true),
239 IsShadowMode(self.is_in_shadow_mode),
240 )?;
241 if !self.is_in_shadow_mode {
242 tracker.cpu = tracker.cpu.saturating_add(cpu_charged);
243 }
244 self.cpu_insns
245 .check_budget_limit(IsShadowMode(self.is_in_shadow_mode))?;
246
247 let mem_charged = self.mem_bytes.charge(
248 ty,
249 iterations,
250 input,
251 IsCpu(false),
252 IsShadowMode(self.is_in_shadow_mode),
253 )?;
254 if !self.is_in_shadow_mode {
255 tracker.mem = tracker.mem.saturating_add(mem_charged);
256 }
257 self.mem_bytes
258 .check_budget_limit(IsShadowMode(self.is_in_shadow_mode))
259 }
260
261 fn get_wasmi_fuel_remaining(&self) -> Result<u64, HostError> {
262 let cpu_remaining = self.cpu_insns.get_remaining();
263 let cost_model = self
264 .cpu_insns
265 .get_cost_model(ContractCostType::WasmInsnExec)?;
266 let cpu_per_fuel = cost_model.const_term.max(1);
267 Ok(cpu_remaining.checked_div(cpu_per_fuel).unwrap_or(0))
278 }
279}
280
281impl Default for BudgetImpl {
284 fn default() -> Self {
285 let mut b = Self {
286 cpu_insns: BudgetDimension::default(),
287 mem_bytes: BudgetDimension::default(),
288 tracker: Default::default(),
289 is_in_shadow_mode: false,
290 fuel_costs: load_calibrated_fuel_costs(),
291 depth_limit: DEFAULT_HOST_DEPTH_LIMIT,
292 };
293
294 for ct in ContractCostType::variants() {
295 let Ok(cpu) = b.cpu_insns.get_cost_model_mut(ct) else {
297 continue;
298 };
299 match ct {
300 ContractCostType::WasmInsnExec => {
305 cpu.const_term = 4;
306 cpu.lin_term = ScaledU64(0);
307 }
308 ContractCostType::MemAlloc => {
312 cpu.const_term = 434;
313 cpu.lin_term = ScaledU64::from_unscaled_u64(1).safe_div(8);
314 }
315 ContractCostType::MemCpy => {
329 cpu.const_term = 42;
330 cpu.lin_term = ScaledU64::from_unscaled_u64(1).safe_div(8);
331 }
332 ContractCostType::MemCmp => {
333 cpu.const_term = 44;
334 cpu.lin_term = ScaledU64::from_unscaled_u64(1).safe_div(8);
335 }
336 ContractCostType::DispatchHostFunction => {
337 cpu.const_term = 310;
338 cpu.lin_term = ScaledU64(0);
339 }
340 ContractCostType::VisitObject => {
341 cpu.const_term = 61;
342 cpu.lin_term = ScaledU64(0);
343 }
344 ContractCostType::ValSer => {
345 cpu.const_term = 230;
346 cpu.lin_term = ScaledU64(29);
347 }
348 ContractCostType::ValDeser => {
349 cpu.const_term = 59052;
350 cpu.lin_term = ScaledU64(4001);
351 }
352 ContractCostType::ComputeSha256Hash => {
353 cpu.const_term = 3738;
354 cpu.lin_term = ScaledU64(7012);
355 }
356 ContractCostType::ComputeEd25519PubKey => {
357 cpu.const_term = 40253;
358 cpu.lin_term = ScaledU64(0);
359 }
360 ContractCostType::VerifyEd25519Sig => {
361 cpu.const_term = 377524;
362 cpu.lin_term = ScaledU64(4068);
363 }
364 ContractCostType::VmInstantiation => {
365 cpu.const_term = 451626;
366 cpu.lin_term = ScaledU64(45405);
367 }
368 ContractCostType::VmCachedInstantiation => {
369 cpu.const_term = 41142;
370 cpu.lin_term = ScaledU64(634);
371 }
372 ContractCostType::InvokeVmFunction => {
373 cpu.const_term = 1948;
374 cpu.lin_term = ScaledU64(0);
375 }
376 ContractCostType::ComputeKeccak256Hash => {
377 cpu.const_term = 3766;
378 cpu.lin_term = ScaledU64(5969);
379 }
380 ContractCostType::DecodeEcdsaCurve256Sig => {
381 cpu.const_term = 710;
382 cpu.lin_term = ScaledU64(0);
383 }
384 ContractCostType::RecoverEcdsaSecp256k1Key => {
385 cpu.const_term = 2315295;
386 cpu.lin_term = ScaledU64(0);
387 }
388 ContractCostType::Int256AddSub => {
389 cpu.const_term = 4404;
390 cpu.lin_term = ScaledU64(0);
391 }
392 ContractCostType::Int256Mul => {
393 cpu.const_term = 4947;
394 cpu.lin_term = ScaledU64(0);
395 }
396 ContractCostType::Int256Div => {
397 cpu.const_term = 4911;
398 cpu.lin_term = ScaledU64(0);
399 }
400 ContractCostType::Int256Pow => {
401 cpu.const_term = 4286;
402 cpu.lin_term = ScaledU64(0);
403 }
404 ContractCostType::Int256Shift => {
405 cpu.const_term = 913;
406 cpu.lin_term = ScaledU64(0);
407 }
408 ContractCostType::ChaCha20DrawBytes => {
409 cpu.const_term = 1058;
410 cpu.lin_term = ScaledU64(501);
411 }
412
413 ContractCostType::ParseWasmInstructions => {
414 cpu.const_term = 73077;
415 cpu.lin_term = ScaledU64(25410);
416 }
417 ContractCostType::ParseWasmFunctions => {
418 cpu.const_term = 0;
419 cpu.lin_term = ScaledU64(540752);
420 }
421 ContractCostType::ParseWasmGlobals => {
422 cpu.const_term = 0;
423 cpu.lin_term = ScaledU64(176363);
424 }
425 ContractCostType::ParseWasmTableEntries => {
426 cpu.const_term = 0;
427 cpu.lin_term = ScaledU64(29989);
428 }
429 ContractCostType::ParseWasmTypes => {
430 cpu.const_term = 0;
431 cpu.lin_term = ScaledU64(1061449);
432 }
433 ContractCostType::ParseWasmDataSegments => {
434 cpu.const_term = 0;
435 cpu.lin_term = ScaledU64(237336);
436 }
437 ContractCostType::ParseWasmElemSegments => {
438 cpu.const_term = 0;
439 cpu.lin_term = ScaledU64(328476);
440 }
441 ContractCostType::ParseWasmImports => {
442 cpu.const_term = 0;
443 cpu.lin_term = ScaledU64(701845);
444 }
445 ContractCostType::ParseWasmExports => {
446 cpu.const_term = 0;
447 cpu.lin_term = ScaledU64(429383);
448 }
449 ContractCostType::ParseWasmDataSegmentBytes => {
450 cpu.const_term = 0;
451 cpu.lin_term = ScaledU64(28);
452 }
453 ContractCostType::InstantiateWasmInstructions => {
454 cpu.const_term = 43030;
455 cpu.lin_term = ScaledU64(0);
456 }
457 ContractCostType::InstantiateWasmFunctions => {
458 cpu.const_term = 0;
459 cpu.lin_term = ScaledU64(7556);
460 }
461 ContractCostType::InstantiateWasmGlobals => {
462 cpu.const_term = 0;
463 cpu.lin_term = ScaledU64(10711);
464 }
465 ContractCostType::InstantiateWasmTableEntries => {
466 cpu.const_term = 0;
467 cpu.lin_term = ScaledU64(3300);
468 }
469 ContractCostType::InstantiateWasmTypes => {
470 cpu.const_term = 0;
471 cpu.lin_term = ScaledU64(0);
472 }
473 ContractCostType::InstantiateWasmDataSegments => {
474 cpu.const_term = 0;
475 cpu.lin_term = ScaledU64(23038);
476 }
477 ContractCostType::InstantiateWasmElemSegments => {
478 cpu.const_term = 0;
479 cpu.lin_term = ScaledU64(42488);
480 }
481 ContractCostType::InstantiateWasmImports => {
482 cpu.const_term = 0;
483 cpu.lin_term = ScaledU64(828974);
484 }
485 ContractCostType::InstantiateWasmExports => {
486 cpu.const_term = 0;
487 cpu.lin_term = ScaledU64(297100);
488 }
489 ContractCostType::InstantiateWasmDataSegmentBytes => {
490 cpu.const_term = 0;
491 cpu.lin_term = ScaledU64(14);
492 }
493 ContractCostType::Sec1DecodePointUncompressed => {
494 cpu.const_term = 1882;
495 cpu.lin_term = ScaledU64(0);
496 }
497 ContractCostType::VerifyEcdsaSecp256r1Sig => {
498 cpu.const_term = 3000906;
499 cpu.lin_term = ScaledU64(0);
500 }
501 ContractCostType::Bls12381EncodeFp => {
502 cpu.const_term = 661;
503 cpu.lin_term = ScaledU64(0);
504 }
505 ContractCostType::Bls12381DecodeFp => {
506 cpu.const_term = 985;
507 cpu.lin_term = ScaledU64(0);
508 }
509 ContractCostType::Bls12381G1CheckPointOnCurve => {
510 cpu.const_term = 1934;
511 cpu.lin_term = ScaledU64(0);
512 }
513 ContractCostType::Bls12381G1CheckPointInSubgroup => {
514 cpu.const_term = 730510;
515 cpu.lin_term = ScaledU64(0);
516 }
517 ContractCostType::Bls12381G2CheckPointOnCurve => {
518 cpu.const_term = 5921;
519 cpu.lin_term = ScaledU64(0);
520 }
521 ContractCostType::Bls12381G2CheckPointInSubgroup => {
522 cpu.const_term = 1057822;
523 cpu.lin_term = ScaledU64(0);
524 }
525 ContractCostType::Bls12381G1ProjectiveToAffine => {
526 cpu.const_term = 92642;
527 cpu.lin_term = ScaledU64(0);
528 }
529 ContractCostType::Bls12381G2ProjectiveToAffine => {
530 cpu.const_term = 100742;
531 cpu.lin_term = ScaledU64(0);
532 }
533 ContractCostType::Bls12381G1Add => {
534 cpu.const_term = 7689;
535 cpu.lin_term = ScaledU64(0);
536 }
537 ContractCostType::Bls12381G1Mul => {
538 cpu.const_term = 2458985;
539 cpu.lin_term = ScaledU64(0);
540 }
541 ContractCostType::Bls12381G1Msm => {
542 cpu.const_term = 2426722;
543 cpu.lin_term = ScaledU64(96397671);
544 }
545 ContractCostType::Bls12381MapFpToG1 => {
546 cpu.const_term = 1541554;
547 cpu.lin_term = ScaledU64(0);
548 }
549 ContractCostType::Bls12381HashToG1 => {
550 cpu.const_term = 3211191;
551 cpu.lin_term = ScaledU64(6713);
552 }
553 ContractCostType::Bls12381G2Add => {
554 cpu.const_term = 25207;
555 cpu.lin_term = ScaledU64(0);
556 }
557 ContractCostType::Bls12381G2Mul => {
558 cpu.const_term = 7873219;
559 cpu.lin_term = ScaledU64(0);
560 }
561 ContractCostType::Bls12381G2Msm => {
562 cpu.const_term = 8035968;
563 cpu.lin_term = ScaledU64(309667335);
564 }
565 ContractCostType::Bls12381MapFp2ToG2 => {
566 cpu.const_term = 2420202;
567 cpu.lin_term = ScaledU64(0);
568 }
569 ContractCostType::Bls12381HashToG2 => {
570 cpu.const_term = 7050564;
571 cpu.lin_term = ScaledU64(6797);
572 }
573 ContractCostType::Bls12381Pairing => {
574 cpu.const_term = 10558948;
575 cpu.lin_term = ScaledU64(632860943);
576 }
577 ContractCostType::Bls12381FrFromU256 => {
578 cpu.const_term = 1994;
579 cpu.lin_term = ScaledU64(0);
580 }
581 ContractCostType::Bls12381FrToU256 => {
582 cpu.const_term = 1155;
583 cpu.lin_term = ScaledU64(0);
584 }
585 ContractCostType::Bls12381FrAddSub => {
586 cpu.const_term = 74;
587 cpu.lin_term = ScaledU64(0);
588 }
589 ContractCostType::Bls12381FrMul => {
590 cpu.const_term = 332;
591 cpu.lin_term = ScaledU64(0);
592 }
593 ContractCostType::Bls12381FrPow => {
594 cpu.const_term = 691;
595 cpu.lin_term = ScaledU64(74558);
596 }
597 ContractCostType::Bls12381FrInv => {
598 cpu.const_term = 35421;
599 cpu.lin_term = ScaledU64(0);
600 }
601 }
602
603 let Ok(mem) = b.mem_bytes.get_cost_model_mut(ct) else {
605 continue;
606 };
607 match ct {
608 ContractCostType::WasmInsnExec => {
611 mem.const_term = 0;
612 mem.lin_term = ScaledU64(0);
613 }
614 ContractCostType::MemAlloc => {
615 mem.const_term = 16;
616 mem.lin_term = ScaledU64::from_unscaled_u64(1);
617 }
618 ContractCostType::MemCpy => {
619 mem.const_term = 0;
620 mem.lin_term = ScaledU64(0);
621 }
622 ContractCostType::MemCmp => {
623 mem.const_term = 0;
624 mem.lin_term = ScaledU64(0);
625 }
626 ContractCostType::DispatchHostFunction => {
627 mem.const_term = 0;
628 mem.lin_term = ScaledU64(0);
629 }
630 ContractCostType::VisitObject => {
631 mem.const_term = 0;
632 mem.lin_term = ScaledU64(0);
633 }
634 ContractCostType::ValSer => {
637 mem.const_term = 242;
638 mem.lin_term = ScaledU64::from_unscaled_u64(3);
639 }
640 ContractCostType::ValDeser => {
641 mem.const_term = 0;
642 mem.lin_term = ScaledU64::from_unscaled_u64(3);
643 }
644 ContractCostType::ComputeSha256Hash => {
645 mem.const_term = 0;
646 mem.lin_term = ScaledU64(0);
647 }
648 ContractCostType::ComputeEd25519PubKey => {
649 mem.const_term = 0;
650 mem.lin_term = ScaledU64(0);
651 }
652 ContractCostType::VerifyEd25519Sig => {
653 mem.const_term = 0;
654 mem.lin_term = ScaledU64(0);
655 }
656 ContractCostType::VmInstantiation => {
657 mem.const_term = 130065;
658 mem.lin_term = ScaledU64(5064);
659 }
660 ContractCostType::VmCachedInstantiation => {
661 mem.const_term = 69472;
662 mem.lin_term = ScaledU64(1217);
663 }
664 ContractCostType::InvokeVmFunction => {
665 mem.const_term = 14;
666 mem.lin_term = ScaledU64(0);
667 }
668 ContractCostType::ComputeKeccak256Hash => {
669 mem.const_term = 0;
670 mem.lin_term = ScaledU64(0);
671 }
672 ContractCostType::DecodeEcdsaCurve256Sig => {
673 mem.const_term = 0;
674 mem.lin_term = ScaledU64(0);
675 }
676 ContractCostType::RecoverEcdsaSecp256k1Key => {
677 mem.const_term = 181;
678 mem.lin_term = ScaledU64(0);
679 }
680 ContractCostType::Int256AddSub => {
681 mem.const_term = 99;
682 mem.lin_term = ScaledU64(0);
683 }
684 ContractCostType::Int256Mul => {
685 mem.const_term = 99;
686 mem.lin_term = ScaledU64(0);
687 }
688 ContractCostType::Int256Div => {
689 mem.const_term = 99;
690 mem.lin_term = ScaledU64(0);
691 }
692 ContractCostType::Int256Pow => {
693 mem.const_term = 99;
694 mem.lin_term = ScaledU64(0);
695 }
696 ContractCostType::Int256Shift => {
697 mem.const_term = 99;
698 mem.lin_term = ScaledU64(0);
699 }
700 ContractCostType::ChaCha20DrawBytes => {
701 mem.const_term = 0;
702 mem.lin_term = ScaledU64(0);
703 }
704
705 ContractCostType::ParseWasmInstructions => {
706 mem.const_term = 17564;
707 mem.lin_term = ScaledU64(6457);
708 }
709 ContractCostType::ParseWasmFunctions => {
710 mem.const_term = 0;
711 mem.lin_term = ScaledU64(47464);
712 }
713 ContractCostType::ParseWasmGlobals => {
714 mem.const_term = 0;
715 mem.lin_term = ScaledU64(13420);
716 }
717 ContractCostType::ParseWasmTableEntries => {
718 mem.const_term = 0;
719 mem.lin_term = ScaledU64(6285);
720 }
721 ContractCostType::ParseWasmTypes => {
722 mem.const_term = 0;
723 mem.lin_term = ScaledU64(64670);
724 }
725 ContractCostType::ParseWasmDataSegments => {
726 mem.const_term = 0;
727 mem.lin_term = ScaledU64(29074);
728 }
729 ContractCostType::ParseWasmElemSegments => {
730 mem.const_term = 0;
731 mem.lin_term = ScaledU64(48095);
732 }
733 ContractCostType::ParseWasmImports => {
734 mem.const_term = 0;
735 mem.lin_term = ScaledU64(103229);
736 }
737 ContractCostType::ParseWasmExports => {
738 mem.const_term = 0;
739 mem.lin_term = ScaledU64(36394);
740 }
741 ContractCostType::ParseWasmDataSegmentBytes => {
742 mem.const_term = 0;
743 mem.lin_term = ScaledU64(257);
744 }
745 ContractCostType::InstantiateWasmInstructions => {
746 mem.const_term = 70704;
747 mem.lin_term = ScaledU64(0);
748 }
749 ContractCostType::InstantiateWasmFunctions => {
750 mem.const_term = 0;
751 mem.lin_term = ScaledU64(14613);
752 }
753 ContractCostType::InstantiateWasmGlobals => {
754 mem.const_term = 0;
755 mem.lin_term = ScaledU64(6833);
756 }
757 ContractCostType::InstantiateWasmTableEntries => {
758 mem.const_term = 0;
759 mem.lin_term = ScaledU64(1025);
760 }
761 ContractCostType::InstantiateWasmTypes => {
762 mem.const_term = 0;
763 mem.lin_term = ScaledU64(0);
764 }
765 ContractCostType::InstantiateWasmDataSegments => {
766 mem.const_term = 0;
767 mem.lin_term = ScaledU64(129632);
768 }
769 ContractCostType::InstantiateWasmElemSegments => {
770 mem.const_term = 0;
771 mem.lin_term = ScaledU64(13665);
772 }
773 ContractCostType::InstantiateWasmImports => {
774 mem.const_term = 0;
775 mem.lin_term = ScaledU64(97637);
776 }
777 ContractCostType::InstantiateWasmExports => {
778 mem.const_term = 0;
779 mem.lin_term = ScaledU64(9176);
780 }
781 ContractCostType::InstantiateWasmDataSegmentBytes => {
782 mem.const_term = 0;
783 mem.lin_term = ScaledU64(126);
784 }
785 ContractCostType::Sec1DecodePointUncompressed => {
786 mem.const_term = 0;
787 mem.lin_term = ScaledU64(0);
788 }
789 ContractCostType::VerifyEcdsaSecp256r1Sig => {
790 mem.const_term = 0;
791 mem.lin_term = ScaledU64(0);
792 }
793 ContractCostType::Bls12381EncodeFp => {
794 mem.const_term = 0;
795 mem.lin_term = ScaledU64(0);
796 }
797 ContractCostType::Bls12381DecodeFp => {
798 mem.const_term = 0;
799 mem.lin_term = ScaledU64(0);
800 }
801 ContractCostType::Bls12381G1CheckPointOnCurve => {
802 mem.const_term = 0;
803 mem.lin_term = ScaledU64(0);
804 }
805 ContractCostType::Bls12381G1CheckPointInSubgroup => {
806 mem.const_term = 0;
807 mem.lin_term = ScaledU64(0);
808 }
809 ContractCostType::Bls12381G2CheckPointOnCurve => {
810 mem.const_term = 0;
811 mem.lin_term = ScaledU64(0);
812 }
813 ContractCostType::Bls12381G2CheckPointInSubgroup => {
814 mem.const_term = 0;
815 mem.lin_term = ScaledU64(0);
816 }
817 ContractCostType::Bls12381G1ProjectiveToAffine => {
818 mem.const_term = 0;
819 mem.lin_term = ScaledU64(0);
820 }
821 ContractCostType::Bls12381G2ProjectiveToAffine => {
822 mem.const_term = 0;
823 mem.lin_term = ScaledU64(0);
824 }
825 ContractCostType::Bls12381G1Add => {
826 mem.const_term = 0;
827 mem.lin_term = ScaledU64(0);
828 }
829 ContractCostType::Bls12381G1Mul => {
830 mem.const_term = 0;
831 mem.lin_term = ScaledU64(0);
832 }
833 ContractCostType::Bls12381G1Msm => {
834 mem.const_term = 109494;
835 mem.lin_term = ScaledU64(354667);
836 }
837 ContractCostType::Bls12381MapFpToG1 => {
838 mem.const_term = 5552;
839 mem.lin_term = ScaledU64(0);
840 }
841 ContractCostType::Bls12381HashToG1 => {
842 mem.const_term = 9424;
843 mem.lin_term = ScaledU64(0);
844 }
845 ContractCostType::Bls12381G2Add => {
846 mem.const_term = 0;
847 mem.lin_term = ScaledU64(0);
848 }
849 ContractCostType::Bls12381G2Mul => {
850 mem.const_term = 0;
851 mem.lin_term = ScaledU64(0);
852 }
853 ContractCostType::Bls12381G2Msm => {
854 mem.const_term = 219654;
855 mem.lin_term = ScaledU64(354667);
856 }
857 ContractCostType::Bls12381MapFp2ToG2 => {
858 mem.const_term = 3344;
859 mem.lin_term = ScaledU64(0);
860 }
861 ContractCostType::Bls12381HashToG2 => {
862 mem.const_term = 6816;
863 mem.lin_term = ScaledU64(0);
864 }
865 ContractCostType::Bls12381Pairing => {
866 mem.const_term = 2204;
867 mem.lin_term = ScaledU64(9340474);
868 }
869 ContractCostType::Bls12381FrFromU256 => {
870 mem.const_term = 0;
871 mem.lin_term = ScaledU64(0);
872 }
873 ContractCostType::Bls12381FrToU256 => {
874 mem.const_term = 248;
875 mem.lin_term = ScaledU64(0);
876 }
877 ContractCostType::Bls12381FrAddSub => {
878 mem.const_term = 0;
879 mem.lin_term = ScaledU64(0);
880 }
881 ContractCostType::Bls12381FrMul => {
882 mem.const_term = 0;
883 mem.lin_term = ScaledU64(0);
884 }
885 ContractCostType::Bls12381FrPow => {
886 mem.const_term = 0;
887 mem.lin_term = ScaledU64(128);
888 }
889 ContractCostType::Bls12381FrInv => {
890 mem.const_term = 0;
891 mem.lin_term = ScaledU64(0);
892 }
893 }
894 }
895
896 b.cpu_insns.reset(limits::DEFAULT_CPU_INSN_LIMIT);
898 b.mem_bytes.reset(limits::DEFAULT_MEM_BYTES_LIMIT);
899 b
900 }
901}
902
903impl Debug for BudgetImpl {
904 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
905 writeln!(f, "{:=<175}", "")?;
906 writeln!(
907 f,
908 "Cpu limit: {}; used: {}",
909 self.cpu_insns.limit, self.cpu_insns.total_count
910 )?;
911 writeln!(
912 f,
913 "Mem limit: {}; used: {}",
914 self.mem_bytes.limit, self.mem_bytes.total_count
915 )?;
916 writeln!(f, "{:=<175}", "")?;
917 writeln!(
918 f,
919 "{:<35}{:<15}{:<15}{:<15}{:<15}{:<20}{:<20}{:<20}{:<20}",
920 "CostType",
921 "iterations",
922 "input",
923 "cpu_insns",
924 "mem_bytes",
925 "const_term_cpu",
926 "lin_term_cpu",
927 "const_term_mem",
928 "lin_term_mem",
929 )?;
930 for ct in ContractCostType::variants() {
931 let i = ct as usize;
932 writeln!(
933 f,
934 "{:<35}{:<15}{:<15}{:<15}{:<15}{:<20}{:<20}{:<20}{:<20}",
935 format!("{:?}", ct),
936 self.tracker.cost_trackers[i].iterations,
937 format!("{:?}", self.tracker.cost_trackers[i].inputs),
938 self.tracker.cost_trackers[i].cpu,
939 self.tracker.cost_trackers[i].mem,
940 self.cpu_insns.cost_models[i].const_term,
941 format!("{}", self.cpu_insns.cost_models[i].lin_term),
942 self.mem_bytes.cost_models[i].const_term,
943 format!("{}", self.mem_bytes.cost_models[i].lin_term),
944 )?;
945 }
946 writeln!(f, "{:=<175}", "")?;
947 writeln!(
948 f,
949 "Internal details (diagnostics info, does not affect fees) "
950 )?;
951 writeln!(
952 f,
953 "Total # times meter was called: {}",
954 self.tracker.meter_count,
955 )?;
956 writeln!(
957 f,
958 "Shadow cpu limit: {}; used: {}",
959 self.cpu_insns.shadow_limit, self.cpu_insns.shadow_total_count
960 )?;
961 writeln!(
962 f,
963 "Shadow mem limit: {}; used: {}",
964 self.mem_bytes.shadow_limit, self.mem_bytes.shadow_total_count
965 )?;
966 writeln!(f, "{:=<175}", "")?;
967 Ok(())
968 }
969}
970
971impl Display for BudgetImpl {
972 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
973 writeln!(f, "{:=<65}", "")?;
974 writeln!(
975 f,
976 "Cpu limit: {}; used: {}",
977 self.cpu_insns.limit, self.cpu_insns.total_count
978 )?;
979 writeln!(
980 f,
981 "Mem limit: {}; used: {}",
982 self.mem_bytes.limit, self.mem_bytes.total_count
983 )?;
984 writeln!(f, "{:=<65}", "")?;
985 writeln!(
986 f,
987 "{:<35}{:<15}{:<15}",
988 "CostType", "cpu_insns", "mem_bytes",
989 )?;
990 for ct in ContractCostType::variants() {
991 let i = ct as usize;
992 writeln!(
993 f,
994 "{:<35}{:<15}{:<15}",
995 format!("{:?}", ct),
996 self.tracker.cost_trackers[i].cpu,
997 self.tracker.cost_trackers[i].mem,
998 )?;
999 }
1000 writeln!(f, "{:=<65}", "")?;
1001 Ok(())
1002 }
1003}
1004
1005#[allow(unused)]
1006#[cfg(test)]
1007impl BudgetImpl {
1008 fn print_default_params_in_cpp(&self) {
1020 println!();
1022 println!();
1023 println!();
1024 for ct in ContractCostType::variants() {
1025 let Ok(cpu) = self.cpu_insns.get_cost_model(ct) else {
1026 continue;
1027 };
1028 println!("case {}:", ct.name());
1029 println!(
1030 "params[val] = ContractCostParamEntry{{ExtensionPoint{{0}}, {}, {}}};",
1031 cpu.const_term, cpu.lin_term.0
1032 );
1033 println!("break;");
1034 }
1035 println!();
1037 println!();
1038 println!();
1039 for ct in ContractCostType::variants() {
1040 let Ok(mem) = self.mem_bytes.get_cost_model(ct) else {
1041 continue;
1042 };
1043 println!("case {}:", ct.name());
1044 println!(
1045 "params[val] = ContractCostParamEntry{{ExtensionPoint{{0}}, {}, {}}};",
1046 mem.const_term, mem.lin_term.0
1047 );
1048 println!("break;");
1049 }
1050 println!();
1051 println!();
1052 println!();
1053 }
1054}
1055
1056#[derive(Clone)]
1057pub struct Budget(pub(crate) Rc<RefCell<BudgetImpl>>);
1058
1059#[allow(clippy::derivable_impls)]
1060impl Default for Budget {
1061 fn default() -> Self {
1062 #[cfg(all(not(target_family = "wasm"), feature = "tracy"))]
1063 let _client = tracy_client::Client::start();
1064 Self(Default::default())
1065 }
1066}
1067
1068impl Debug for Budget {
1069 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1070 writeln!(f, "{:?}", self.0.try_borrow().map_err(|_| std::fmt::Error)?)
1071 }
1072}
1073
1074impl Display for Budget {
1075 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1076 writeln!(f, "{}", self.0.try_borrow().map_err(|_| std::fmt::Error)?)
1077 }
1078}
1079
1080pub trait AsBudget: Clone {
1081 fn as_budget(&self) -> &Budget;
1082}
1083
1084impl AsBudget for Budget {
1085 fn as_budget(&self) -> &Budget {
1086 self
1087 }
1088}
1089
1090impl AsBudget for &Budget {
1091 fn as_budget(&self) -> &Budget {
1092 self
1093 }
1094}
1095
1096impl AsBudget for Host {
1097 fn as_budget(&self) -> &Budget {
1098 self.budget_ref()
1099 }
1100}
1101
1102impl AsBudget for &Host {
1103 fn as_budget(&self) -> &Budget {
1104 self.budget_ref()
1105 }
1106}
1107
1108impl Budget {
1109 pub fn try_from_configs(
1111 cpu_limit: u64,
1112 mem_limit: u64,
1113 cpu_cost_params: ContractCostParams,
1114 mem_cost_params: ContractCostParams,
1115 ) -> Result<Self, HostError> {
1116 Ok(Self(Rc::new(RefCell::new(BudgetImpl::try_from_configs(
1117 cpu_limit,
1118 mem_limit,
1119 cpu_cost_params,
1120 mem_cost_params,
1121 )?))))
1122 }
1123
1124 fn with_mut_budget<T, F>(&self, f: F) -> Result<T, HostError>
1126 where
1127 F: FnOnce(RefMut<BudgetImpl>) -> Result<T, HostError>,
1128 {
1129 f(self.0.try_borrow_mut_or_err()?)
1130 }
1131
1132 pub fn bulk_charge(
1139 &self,
1140 ty: ContractCostType,
1141 iterations: u64,
1142 input: Option<u64>,
1143 ) -> Result<(), HostError> {
1144 self.0
1145 .try_borrow_mut_or_err()?
1146 .charge(ty, iterations, input)
1147 }
1148
1149 pub fn charge(&self, ty: ContractCostType, input: Option<u64>) -> Result<(), HostError> {
1155 self.0.try_borrow_mut_or_err()?.charge(ty, 1, input)
1156 }
1157
1158 pub(crate) fn with_shadow_mode<T, F>(&self, f: F)
1167 where
1168 F: FnOnce() -> Result<T, HostError>,
1169 {
1170 let mut prev = false;
1171
1172 if self
1173 .with_mut_budget(|mut b| {
1174 prev = b.is_in_shadow_mode;
1175 b.is_in_shadow_mode = true;
1176 b.cpu_insns.check_budget_limit(IsShadowMode(true))?;
1177 b.mem_bytes.check_budget_limit(IsShadowMode(true))
1178 })
1179 .is_ok()
1180 {
1181 let _ = f();
1182 }
1183
1184 let _ = self.with_mut_budget(|mut b| {
1185 b.is_in_shadow_mode = prev;
1186 Ok(())
1187 });
1188 }
1189
1190 pub(crate) fn is_in_shadow_mode(&self) -> Result<bool, HostError> {
1191 Ok(self.0.try_borrow_or_err()?.is_in_shadow_mode)
1192 }
1193
1194 pub(crate) fn set_shadow_limits(&self, cpu: u64, mem: u64) -> Result<(), HostError> {
1195 self.0.try_borrow_mut_or_err()?.cpu_insns.shadow_limit = cpu;
1196 self.0.try_borrow_mut_or_err()?.mem_bytes.shadow_limit = mem;
1197 Ok(())
1198 }
1199
1200 pub(crate) fn ensure_shadow_cpu_limit_factor(&self, factor: u64) -> Result<(), HostError> {
1201 let mut b = self.0.try_borrow_mut_or_err()?;
1202 b.cpu_insns.shadow_limit = b.cpu_insns.limit.saturating_mul(factor);
1203 Ok(())
1204 }
1205
1206 pub(crate) fn ensure_shadow_mem_limit_factor(&self, factor: u64) -> Result<(), HostError> {
1207 let mut b = self.0.try_borrow_mut_or_err()?;
1208 b.mem_bytes.shadow_limit = b.mem_bytes.limit.saturating_mul(factor);
1209 Ok(())
1210 }
1211
1212 pub fn get_tracker(&self, ty: ContractCostType) -> Result<CostTracker, HostError> {
1213 self.0
1214 .try_borrow_or_err()?
1215 .tracker
1216 .cost_trackers
1217 .get(ty as usize)
1218 .map(|x| *x)
1219 .ok_or_else(|| (ScErrorType::Budget, ScErrorCode::InternalError).into())
1220 }
1221
1222 pub fn get_time(&self, ty: ContractCostType) -> Result<u64, HostError> {
1223 self.0.try_borrow_or_err()?.tracker.get_time(ty)
1224 }
1225
1226 pub fn track_time(&self, ty: ContractCostType, duration: u64) -> Result<(), HostError> {
1227 self.0
1228 .try_borrow_mut_or_err()?
1229 .tracker
1230 .track_time(ty, duration)
1231 }
1232
1233 pub fn get_cpu_insns_consumed(&self) -> Result<u64, HostError> {
1234 Ok(self.0.try_borrow_or_err()?.cpu_insns.get_total_count())
1235 }
1236
1237 pub fn get_mem_bytes_consumed(&self) -> Result<u64, HostError> {
1238 Ok(self.0.try_borrow_or_err()?.mem_bytes.get_total_count())
1239 }
1240
1241 pub fn get_cpu_insns_remaining(&self) -> Result<u64, HostError> {
1242 Ok(self.0.try_borrow_or_err()?.cpu_insns.get_remaining())
1243 }
1244
1245 pub fn get_mem_bytes_remaining(&self) -> Result<u64, HostError> {
1246 Ok(self.0.try_borrow_or_err()?.mem_bytes.get_remaining())
1247 }
1248
1249 pub(crate) fn get_wasmi_fuel_remaining(&self) -> Result<u64, HostError> {
1250 self.0.try_borrow_mut_or_err()?.get_wasmi_fuel_remaining()
1251 }
1252
1253 pub fn reset_default(&self) -> Result<(), HostError> {
1254 *self.0.try_borrow_mut_or_err()? = BudgetImpl::default();
1255 Ok(())
1256 }
1257}
1258
1259#[test]
1260fn test_budget_initialization() -> Result<(), HostError> {
1261 use crate::xdr::{ContractCostParamEntry, ExtensionPoint};
1262 let cpu_cost_params = ContractCostParams(
1263 vec![
1264 ContractCostParamEntry {
1265 ext: ExtensionPoint::V0,
1266 const_term: 35,
1267 linear_term: 36,
1268 },
1269 ContractCostParamEntry {
1270 ext: ExtensionPoint::V0,
1271 const_term: 37,
1272 linear_term: 38,
1273 },
1274 ]
1275 .try_into()
1276 .unwrap(),
1277 );
1278 let mem_cost_params = ContractCostParams(
1279 vec![
1280 ContractCostParamEntry {
1281 ext: ExtensionPoint::V0,
1282 const_term: 39,
1283 linear_term: 40,
1284 },
1285 ContractCostParamEntry {
1286 ext: ExtensionPoint::V0,
1287 const_term: 41,
1288 linear_term: 42,
1289 },
1290 ContractCostParamEntry {
1291 ext: ExtensionPoint::V0,
1292 const_term: 43,
1293 linear_term: 44,
1294 },
1295 ]
1296 .try_into()
1297 .unwrap(),
1298 );
1299
1300 let budget = Budget::try_from_configs(100, 100, cpu_cost_params, mem_cost_params)?;
1301 assert_eq!(
1302 budget.0.try_borrow_or_err()?.cpu_insns.cost_models.len(),
1303 ContractCostType::variants().len()
1304 );
1305 assert_eq!(
1306 budget.0.try_borrow_or_err()?.mem_bytes.cost_models.len(),
1307 ContractCostType::variants().len()
1308 );
1309 assert_eq!(
1310 budget.0.try_borrow_or_err()?.tracker.cost_trackers.len(),
1311 ContractCostType::variants().len()
1312 );
1313 assert_eq!(
1314 budget.0.try_borrow_or_err()?.tracker.time_tracker.len(),
1315 ContractCostType::variants().len()
1316 );
1317
1318 Ok(())
1319}