1use std::cell::RefMut;
2
3use soroban_env_common::Env;
4
5use crate::{
6 e2e_invoke::{encode_contract_events, entry_size_for_rent},
7 fees::{FeeConfiguration, DATA_SIZE_1KB_INCREMENT, INSTRUCTIONS_INCREMENT, TTL_ENTRY_SIZE},
8 ledger_info::get_key_durability,
9 storage::{is_persistent_key, AccessType, Storage},
10 xdr::{ContractDataDurability, LedgerKey, ScErrorCode, ScErrorType},
11};
12
13use super::{metered_xdr::metered_write_xdr, Host, HostError};
14
15#[derive(Default, Clone, Debug, Eq, PartialEq)]
21pub struct InvocationResources {
22 pub instructions: i64,
24 pub mem_bytes: i64,
29 pub disk_read_entries: u32,
37 pub memory_read_entries: u32,
45 pub write_entries: u32,
48 pub disk_read_bytes: u32,
56 pub write_bytes: u32,
58 pub contract_events_size_bytes: u32,
60 pub persistent_rent_ledger_bytes: i64,
64 pub persistent_entry_rent_bumps: u32,
66 pub temporary_rent_ledger_bytes: i64,
70 pub temporary_entry_rent_bumps: u32,
72}
73
74#[derive(Default, Clone, Debug, Eq, PartialEq)]
80pub struct FeeEstimate {
81 pub total: i64,
83 pub instructions: i64,
85 pub disk_read_entries: i64,
87 pub write_entries: i64,
89 pub disk_read_bytes: i64,
91 pub write_bytes: i64,
93 pub contract_events: i64,
95 pub persistent_entry_rent: i64,
97 pub temporary_entry_rent: i64,
99}
100
101impl InvocationResources {
102 pub fn estimate_fees(
111 &self,
112 fee_config: &FeeConfiguration,
113 fee_per_rent_1kb: i64,
114 persistent_rent_rate_denominator: i64,
115 temporary_rent_rate_denominator: i64,
116 ) -> FeeEstimate {
117 let instructions = compute_fee_per_increment(
118 self.instructions,
119 fee_config.fee_per_instruction_increment,
120 INSTRUCTIONS_INCREMENT,
121 );
122 let disk_read_entries = fee_config.fee_per_disk_read_entry.saturating_mul(
123 self.disk_read_entries
124 .saturating_add(self.write_entries)
125 .into(),
126 );
127 let write_entries = fee_config
128 .fee_per_write_entry
129 .saturating_mul(self.write_entries.into());
130 let disk_read_bytes = compute_fee_per_increment(
131 self.disk_read_bytes.into(),
132 fee_config.fee_per_disk_read_1kb,
133 DATA_SIZE_1KB_INCREMENT,
134 );
135 let write_bytes = compute_fee_per_increment(
136 self.write_bytes.into(),
137 fee_config.fee_per_write_1kb,
138 DATA_SIZE_1KB_INCREMENT,
139 );
140 let contract_events = compute_fee_per_increment(
141 self.contract_events_size_bytes.into(),
142 fee_config.fee_per_contract_event_1kb,
143 DATA_SIZE_1KB_INCREMENT,
144 );
145
146 let mut persistent_entry_ttl_entry_writes = fee_config
147 .fee_per_write_entry
148 .saturating_mul(self.persistent_entry_rent_bumps.into());
149 persistent_entry_ttl_entry_writes =
150 persistent_entry_ttl_entry_writes.saturating_add(compute_fee_per_increment(
151 (TTL_ENTRY_SIZE as i64).saturating_mul(self.persistent_entry_rent_bumps.into()),
152 fee_config.fee_per_write_1kb,
153 DATA_SIZE_1KB_INCREMENT,
154 ));
155 let mut temp_entry_ttl_entry_writes = fee_config
156 .fee_per_write_entry
157 .saturating_mul(self.temporary_entry_rent_bumps.into());
158 temp_entry_ttl_entry_writes =
159 temp_entry_ttl_entry_writes.saturating_add(compute_fee_per_increment(
160 (TTL_ENTRY_SIZE as i64).saturating_mul(self.temporary_entry_rent_bumps.into()),
161 fee_config.fee_per_write_1kb,
162 DATA_SIZE_1KB_INCREMENT,
163 ));
164
165 let persistent_entry_rent = compute_fee_per_increment(
166 self.persistent_rent_ledger_bytes,
167 fee_per_rent_1kb,
168 DATA_SIZE_1KB_INCREMENT.saturating_mul(persistent_rent_rate_denominator),
169 )
170 .saturating_add(persistent_entry_ttl_entry_writes);
171 let temporary_entry_rent = compute_fee_per_increment(
172 self.temporary_rent_ledger_bytes,
173 fee_per_rent_1kb,
174 DATA_SIZE_1KB_INCREMENT.saturating_mul(temporary_rent_rate_denominator),
175 )
176 .saturating_add(temp_entry_ttl_entry_writes);
177 let total = instructions
178 .saturating_add(disk_read_entries)
179 .saturating_add(write_entries)
180 .saturating_add(disk_read_bytes)
181 .saturating_add(write_bytes)
182 .saturating_add(contract_events)
183 .saturating_add(persistent_entry_rent)
184 .saturating_add(temporary_entry_rent);
185 FeeEstimate {
186 total,
187 instructions,
188 disk_read_entries,
189 write_entries,
190 disk_read_bytes,
191 write_bytes,
192 contract_events,
193 persistent_entry_rent,
194 temporary_entry_rent,
195 }
196 }
197}
198
199#[derive(Default, Clone)]
207pub(crate) struct InvocationMeter {
208 active: bool,
209 enabled: bool,
210 storage_snapshot: Option<Storage>,
211 invocation_resources: Option<InvocationResources>,
212}
213
214pub(crate) struct InvocationMeterScope<'a> {
217 meter: RefMut<'a, InvocationMeter>,
218 host: &'a Host,
219}
220
221impl Drop for InvocationMeterScope<'_> {
222 fn drop(&mut self) {
223 self.meter.finish_invocation(self.host);
224 }
225}
226
227impl InvocationMeter {
228 pub(crate) fn get_invocation_resources(&self) -> Option<InvocationResources> {
230 self.invocation_resources.clone()
231 }
232
233 fn start_invocation<'a>(
234 mut scope: RefMut<'a, InvocationMeter>,
235 host: &'a Host,
236 ) -> Result<Option<InvocationMeterScope<'a>>, HostError> {
237 if scope.active || !scope.enabled {
238 return Ok(None);
239 }
240 scope.storage_snapshot = Some(host.try_borrow_storage()?.clone());
241 host.try_borrow_storage_mut()?.reset_footprint();
245 host.try_borrow_events_mut()?.clear();
246 host.budget_ref().reset()?;
247 Ok(Some(InvocationMeterScope { meter: scope, host }))
248 }
249
250 fn finish_invocation(&mut self, host: &Host) -> () {
251 self.active = false;
252 let mut invocation_resources = InvocationResources::default();
253 let budget = host.budget_ref();
254 invocation_resources.instructions =
255 budget.get_cpu_insns_consumed().unwrap_or_default() as i64;
256 invocation_resources.mem_bytes = budget.get_mem_bytes_consumed().unwrap_or_default() as i64;
257
258 let measure_res = budget.with_observable_shadow_mode(|| {
259 self.try_measure_resources(&mut invocation_resources, host)
260 });
261
262 if measure_res.is_ok() {
263 self.invocation_resources = Some(invocation_resources);
264 } else {
265 self.invocation_resources = None;
266 }
267
268 budget.with_shadow_mode(|| host.ensure_module_cache_contains_host_storage_contracts());
272
273 self.storage_snapshot = None;
274 }
275
276 fn try_measure_resources(
277 &mut self,
278 invocation_resources: &mut InvocationResources,
279 host: &Host,
280 ) -> Result<(), HostError> {
281 let prev_storage = self.storage_snapshot.as_mut().ok_or_else(|| {
282 host.err(
283 ScErrorType::Context,
284 ScErrorCode::InternalError,
285 "missing a storage snapshot in metering scope, `open` must be called before `close`",
286 &[],
287 )
288 })?;
289
290 let mut curr_storage = host.try_borrow_storage_mut()?;
291 let footprint = curr_storage.footprint.clone();
292 let curr_ledger_seq: u32 = host.get_ledger_sequence()?.into();
293 for (key, access_type) in footprint.0.iter(host.budget_ref())? {
294 let maybe_init_entry = prev_storage.get_from_map(key, host)?;
295 let mut init_entry_size_for_rent = 0;
296 let mut init_live_until_ledger = curr_ledger_seq;
297 let mut is_disk_read = match key.as_ref() {
298 LedgerKey::ContractData(_) | LedgerKey::ContractCode(_) => false,
299 _ => true,
300 };
301 if let Some((init_entry, init_entry_live_until)) = maybe_init_entry {
302 if let Some(live_until) = init_entry_live_until {
303 if live_until >= curr_ledger_seq {
304 init_live_until_ledger = live_until;
307 } else {
308 is_disk_read = is_persistent_key(key.as_ref());
312 }
313 }
314
315 let mut buf = Vec::<u8>::new();
316 metered_write_xdr(host.budget_ref(), init_entry.as_ref(), &mut buf)?;
317 if is_disk_read {
318 invocation_resources.disk_read_bytes += buf.len() as u32;
319 }
320 init_entry_size_for_rent =
321 entry_size_for_rent(host.budget_ref(), &init_entry, buf.len() as u32)?;
322 }
323 let mut entry_size = 0;
324 let mut new_entry_size_for_rent = 0;
325 let mut entry_live_until_ledger = None;
326 let maybe_entry = curr_storage.try_get_full(key, host, None)?;
327 if let Some((entry, entry_live_until)) = maybe_entry {
328 let mut buf = Vec::<u8>::new();
329 metered_write_xdr(host.budget_ref(), entry.as_ref(), &mut buf)?;
330 entry_size = buf.len() as u32;
331 new_entry_size_for_rent =
332 entry_size_for_rent(host.budget_ref(), &entry, entry_size)?;
333 entry_live_until_ledger = entry_live_until;
334 }
335 if is_disk_read {
336 invocation_resources.disk_read_entries += 1;
337 } else {
338 invocation_resources.memory_read_entries += 1;
339 }
340 if matches!(access_type, AccessType::ReadWrite) {
341 invocation_resources.write_entries += 1;
342 invocation_resources.write_bytes += entry_size;
343 }
344
345 if let Some(new_live_until) = entry_live_until_ledger {
346 let extension_ledgers = (new_live_until - init_live_until_ledger) as i64;
347 let rent_size_delta = if new_entry_size_for_rent > init_entry_size_for_rent {
348 (new_entry_size_for_rent - init_entry_size_for_rent) as i64
349 } else {
350 0
351 };
352 let existing_ledgers = (init_live_until_ledger - curr_ledger_seq) as i64;
353 let rent_ledger_bytes = existing_ledgers * rent_size_delta
354 + extension_ledgers * (new_entry_size_for_rent as i64);
355 if rent_ledger_bytes > 0 {
356 match get_key_durability(key.as_ref()) {
357 Some(ContractDataDurability::Temporary) => {
358 invocation_resources.temporary_rent_ledger_bytes += rent_ledger_bytes;
359 invocation_resources.temporary_entry_rent_bumps += 1;
360 }
361 Some(ContractDataDurability::Persistent) => {
362 invocation_resources.persistent_rent_ledger_bytes += rent_ledger_bytes;
363 invocation_resources.persistent_entry_rent_bumps += 1;
364 }
365 None => (),
366 }
367 }
368 }
369 }
370 let events = host.try_borrow_events()?.externalize(&host)?;
371 let encoded_contract_events = encode_contract_events(host.budget_ref(), &events)?;
372 for event in &encoded_contract_events {
373 invocation_resources.contract_events_size_bytes += event.len() as u32;
374 }
375 Ok(())
376 }
377}
378
379impl Host {
380 pub(crate) fn maybe_meter_invocation(
386 &self,
387 ) -> Result<Option<InvocationMeterScope<'_>>, HostError> {
388 if let Ok(scope) = self.0.invocation_meter.try_borrow_mut() {
393 InvocationMeter::start_invocation(scope, self)
394 } else {
395 Ok(None)
396 }
397 }
398
399 pub fn enable_invocation_metering(&self) {
401 if let Ok(mut meter) = self.0.invocation_meter.try_borrow_mut() {
402 meter.enabled = true;
403 }
404 }
405}
406
407fn compute_fee_per_increment(resource_value: i64, fee_rate: i64, increment: i64) -> i64 {
408 num_integer::div_ceil(resource_value.saturating_mul(fee_rate), increment.max(1))
409}
410
411#[cfg(test)]
412mod test {
413 use super::*;
414 use crate::{Symbol, TryFromVal, TryIntoVal};
415 use expect_test::expect;
416 use soroban_test_wasms::CONTRACT_STORAGE;
417
418 fn assert_resources_equal_to_budget(host: &Host) {
419 assert_eq!(
420 host.get_last_invocation_resources().unwrap().instructions as u64,
421 host.budget_ref().get_cpu_insns_consumed().unwrap()
422 );
423 assert_eq!(
424 host.get_last_invocation_resources().unwrap().mem_bytes as u64,
425 host.budget_ref().get_mem_bytes_consumed().unwrap()
426 );
427 }
428
429 #[test]
434 fn test_invocation_resource_metering() {
435 let host = Host::test_host_with_recording_footprint();
436 host.enable_invocation_metering();
437 host.enable_debug().unwrap();
438 host.with_mut_ledger_info(|li| {
439 li.sequence_number = 100;
440 li.max_entry_ttl = 10000;
441 li.min_persistent_entry_ttl = 1000;
442 li.min_temp_entry_ttl = 16;
443 })
444 .unwrap();
445
446 let contract_id = host.register_test_contract_wasm(CONTRACT_STORAGE);
447 expect![[r#"
450 InvocationResources {
451 instructions: 4199640,
452 mem_bytes: 2863204,
453 disk_read_entries: 0,
454 memory_read_entries: 2,
455 write_entries: 2,
456 disk_read_bytes: 0,
457 write_bytes: 3132,
458 contract_events_size_bytes: 0,
459 persistent_rent_ledger_bytes: 80531388,
460 persistent_entry_rent_bumps: 2,
461 temporary_rent_ledger_bytes: 0,
462 temporary_entry_rent_bumps: 0,
463 }"#]]
464 .assert_eq(format!("{:#?}", host.get_last_invocation_resources().unwrap()).as_str());
465 assert_resources_equal_to_budget(&host);
466
467 let key = Symbol::try_from_small_str("key_1").unwrap();
468
469 let _ = &host
471 .call(
472 contract_id,
473 Symbol::try_from_val(&host, &"has_persistent").unwrap(),
474 test_vec![&host, key].into(),
475 )
476 .unwrap();
477 expect![[r#"
478 InvocationResources {
479 instructions: 316637,
480 mem_bytes: 1134859,
481 disk_read_entries: 0,
482 memory_read_entries: 3,
483 write_entries: 0,
484 disk_read_bytes: 0,
485 write_bytes: 0,
486 contract_events_size_bytes: 0,
487 persistent_rent_ledger_bytes: 0,
488 persistent_entry_rent_bumps: 0,
489 temporary_rent_ledger_bytes: 0,
490 temporary_entry_rent_bumps: 0,
491 }"#]]
492 .assert_eq(format!("{:#?}", host.get_last_invocation_resources().unwrap()).as_str());
493 assert_resources_equal_to_budget(&host);
494
495 let _ = &host
497 .try_call(
498 contract_id,
499 Symbol::try_from_val(&host, &"put_persistent").unwrap(),
500 test_vec![&host, key, 1234_u64].into(),
501 )
502 .unwrap();
503 expect![[r#"
504 InvocationResources {
505 instructions: 320246,
506 mem_bytes: 1135322,
507 disk_read_entries: 0,
508 memory_read_entries: 3,
509 write_entries: 1,
510 disk_read_bytes: 0,
511 write_bytes: 84,
512 contract_events_size_bytes: 0,
513 persistent_rent_ledger_bytes: 83916,
514 persistent_entry_rent_bumps: 1,
515 temporary_rent_ledger_bytes: 0,
516 temporary_entry_rent_bumps: 0,
517 }"#]]
518 .assert_eq(format!("{:#?}", host.get_last_invocation_resources().unwrap()).as_str());
519 assert_resources_equal_to_budget(&host);
520
521 let _ = &host
523 .call(
524 contract_id,
525 Symbol::try_from_val(&host, &"has_persistent").unwrap(),
526 test_vec![&host, key].into(),
527 )
528 .unwrap();
529 expect![[r#"
530 InvocationResources {
531 instructions: 315936,
532 mem_bytes: 1134707,
533 disk_read_entries: 0,
534 memory_read_entries: 3,
535 write_entries: 0,
536 disk_read_bytes: 0,
537 write_bytes: 0,
538 contract_events_size_bytes: 0,
539 persistent_rent_ledger_bytes: 0,
540 persistent_entry_rent_bumps: 0,
541 temporary_rent_ledger_bytes: 0,
542 temporary_entry_rent_bumps: 0,
543 }"#]]
544 .assert_eq(format!("{:#?}", host.get_last_invocation_resources().unwrap()).as_str());
545 assert_resources_equal_to_budget(&host);
546
547 let _ = &host
549 .try_call(
550 contract_id,
551 Symbol::try_from_val(&host, &"put_temporary").unwrap(),
552 test_vec![&host, key, 1234_u64].into(),
553 )
554 .unwrap();
555 expect![[r#"
556 InvocationResources {
557 instructions: 322157,
558 mem_bytes: 1135678,
559 disk_read_entries: 0,
560 memory_read_entries: 3,
561 write_entries: 1,
562 disk_read_bytes: 0,
563 write_bytes: 84,
564 contract_events_size_bytes: 0,
565 persistent_rent_ledger_bytes: 0,
566 persistent_entry_rent_bumps: 0,
567 temporary_rent_ledger_bytes: 1260,
568 temporary_entry_rent_bumps: 1,
569 }"#]]
570 .assert_eq(format!("{:#?}", host.get_last_invocation_resources().unwrap()).as_str());
571 assert_resources_equal_to_budget(&host);
572
573 let _ = &host
575 .try_call(
576 contract_id,
577 Symbol::try_from_val(&host, &"has_temporary").unwrap(),
578 test_vec![&host, key].into(),
579 )
580 .unwrap();
581 expect![[r#"
582 InvocationResources {
583 instructions: 316476,
584 mem_bytes: 1134775,
585 disk_read_entries: 0,
586 memory_read_entries: 3,
587 write_entries: 0,
588 disk_read_bytes: 0,
589 write_bytes: 0,
590 contract_events_size_bytes: 0,
591 persistent_rent_ledger_bytes: 0,
592 persistent_entry_rent_bumps: 0,
593 temporary_rent_ledger_bytes: 0,
594 temporary_entry_rent_bumps: 0,
595 }"#]]
596 .assert_eq(format!("{:#?}", host.get_last_invocation_resources().unwrap()).as_str());
597 assert_resources_equal_to_budget(&host);
598
599 let _ = &host
601 .call(
602 contract_id,
603 Symbol::try_from_val(&host, &"extend_persistent").unwrap(),
604 test_vec![&host, key, &5000_u32, &5000_u32].into(),
605 )
606 .unwrap();
607 expect![[r#"
608 InvocationResources {
609 instructions: 317701,
610 mem_bytes: 1135127,
611 disk_read_entries: 0,
612 memory_read_entries: 3,
613 write_entries: 0,
614 disk_read_bytes: 0,
615 write_bytes: 0,
616 contract_events_size_bytes: 0,
617 persistent_rent_ledger_bytes: 336084,
618 persistent_entry_rent_bumps: 1,
619 temporary_rent_ledger_bytes: 0,
620 temporary_entry_rent_bumps: 0,
621 }"#]]
622 .assert_eq(format!("{:#?}", host.get_last_invocation_resources().unwrap()).as_str());
623 assert_resources_equal_to_budget(&host);
624
625 let _ = &host
627 .call(
628 contract_id,
629 Symbol::try_from_val(&host, &"extend_temporary").unwrap(),
630 test_vec![&host, key, &3000_u32, &3000_u32].into(),
631 )
632 .unwrap();
633 expect![[r#"
634 InvocationResources {
635 instructions: 318103,
636 mem_bytes: 1135127,
637 disk_read_entries: 0,
638 memory_read_entries: 3,
639 write_entries: 0,
640 disk_read_bytes: 0,
641 write_bytes: 0,
642 contract_events_size_bytes: 0,
643 persistent_rent_ledger_bytes: 0,
644 persistent_entry_rent_bumps: 0,
645 temporary_rent_ledger_bytes: 250740,
646 temporary_entry_rent_bumps: 1,
647 }"#]]
648 .assert_eq(format!("{:#?}", host.get_last_invocation_resources().unwrap()).as_str());
649 assert_resources_equal_to_budget(&host);
650
651 let non_existent_key = Symbol::try_from_small_str("non_exist").unwrap();
653 let res = &host.call(
654 contract_id,
655 Symbol::try_from_val(&host, &"extend_persistent").unwrap(),
656 test_vec![&host, non_existent_key, &5000_u32, &5000_u32].into(),
657 );
658 assert!(res.is_err());
659 expect![[r#"
660 InvocationResources {
661 instructions: 317540,
662 mem_bytes: 1135195,
663 disk_read_entries: 0,
664 memory_read_entries: 3,
665 write_entries: 0,
666 disk_read_bytes: 0,
667 write_bytes: 0,
668 contract_events_size_bytes: 0,
669 persistent_rent_ledger_bytes: 0,
670 persistent_entry_rent_bumps: 0,
671 temporary_rent_ledger_bytes: 0,
672 temporary_entry_rent_bumps: 0,
673 }"#]]
674 .assert_eq(format!("{:#?}", host.get_last_invocation_resources().unwrap()).as_str());
675 assert_resources_equal_to_budget(&host);
676
677 host.with_mut_ledger_info(|li| {
680 li.sequence_number += li.min_persistent_entry_ttl;
681 })
682 .unwrap();
683 let _ = &host
686 .call(
687 contract_id,
688 Symbol::try_from_val(&host, &"has_persistent").unwrap(),
689 test_vec![&host, key].into(),
690 )
691 .unwrap();
692 expect![[r#"
693 InvocationResources {
694 instructions: 320711,
695 mem_bytes: 1135662,
696 disk_read_entries: 2,
697 memory_read_entries: 1,
698 write_entries: 2,
699 disk_read_bytes: 3132,
700 write_bytes: 3132,
701 contract_events_size_bytes: 0,
702 persistent_rent_ledger_bytes: 80531388,
703 persistent_entry_rent_bumps: 2,
704 temporary_rent_ledger_bytes: 0,
705 temporary_entry_rent_bumps: 0,
706 }"#]]
707 .assert_eq(format!("{:#?}", host.get_last_invocation_resources().unwrap()).as_str());
708 assert_resources_equal_to_budget(&host);
709
710 host.with_mut_ledger_info(|li| {
713 li.sequence_number += 5000 - li.min_persistent_entry_ttl + 1;
714 })
715 .unwrap();
716 let _ = &host
718 .call(
719 contract_id,
720 Symbol::try_from_val(&host, &"has_persistent").unwrap(),
721 test_vec![&host, key].into(),
722 )
723 .unwrap();
724 expect![[r#"
725 InvocationResources {
726 instructions: 323248,
727 mem_bytes: 1136109,
728 disk_read_entries: 3,
729 memory_read_entries: 0,
730 write_entries: 3,
731 disk_read_bytes: 3216,
732 write_bytes: 3216,
733 contract_events_size_bytes: 0,
734 persistent_rent_ledger_bytes: 80615304,
735 persistent_entry_rent_bumps: 3,
736 temporary_rent_ledger_bytes: 0,
737 temporary_entry_rent_bumps: 0,
738 }"#]]
739 .assert_eq(format!("{:#?}", host.get_last_invocation_resources().unwrap()).as_str());
740 assert_resources_equal_to_budget(&host);
741 }
742
743 #[test]
744 fn test_resource_fee_estimation() {
745 assert_eq!(
747 InvocationResources {
748 instructions: 0,
749 mem_bytes: 100_000,
750 disk_read_entries: 0,
751 memory_read_entries: 100,
752 write_entries: 0,
753 disk_read_bytes: 0,
754 write_bytes: 0,
755 contract_events_size_bytes: 0,
756 persistent_rent_ledger_bytes: 0,
757 persistent_entry_rent_bumps: 0,
758 temporary_rent_ledger_bytes: 0,
759 temporary_entry_rent_bumps: 0,
760 }
761 .estimate_fees(
762 &FeeConfiguration {
763 fee_per_instruction_increment: 100,
764 fee_per_disk_read_entry: 100,
765 fee_per_write_entry: 100,
766 fee_per_disk_read_1kb: 100,
767 fee_per_write_1kb: 100,
768 fee_per_historical_1kb: 100,
769 fee_per_contract_event_1kb: 100,
770 fee_per_transaction_size_1kb: 100,
771 },
772 100,
773 1,
774 1
775 ),
776 FeeEstimate {
777 total: 0,
778 instructions: 0,
779 disk_read_entries: 0,
780 write_entries: 0,
781 disk_read_bytes: 0,
782 write_bytes: 0,
783 contract_events: 0,
784 persistent_entry_rent: 0,
785 temporary_entry_rent: 0,
786 }
787 );
788
789 assert_eq!(
791 InvocationResources {
792 instructions: 1,
793 mem_bytes: 100_000,
794 disk_read_entries: 1,
795 memory_read_entries: 100,
796 write_entries: 1,
797 disk_read_bytes: 1,
798 write_bytes: 1,
799 contract_events_size_bytes: 1,
800 persistent_rent_ledger_bytes: 1,
801 persistent_entry_rent_bumps: 1,
802 temporary_rent_ledger_bytes: 1,
803 temporary_entry_rent_bumps: 1
804 }
805 .estimate_fees(
806 &FeeConfiguration {
807 fee_per_instruction_increment: 100,
808 fee_per_disk_read_entry: 100,
809 fee_per_write_entry: 100,
810 fee_per_disk_read_1kb: 100,
811 fee_per_write_1kb: 100,
812 fee_per_historical_1kb: 100,
813 fee_per_contract_event_1kb: 100,
814 fee_per_transaction_size_1kb: 100,
815 },
816 100,
817 1,
818 1
819 ),
820 FeeEstimate {
821 total: 516,
822 instructions: 1,
823 disk_read_entries: 200,
824 write_entries: 100,
825 disk_read_bytes: 1,
826 write_bytes: 1,
827 contract_events: 1,
828 persistent_entry_rent: 106,
829 temporary_entry_rent: 106
830 }
831 );
832
833 assert_eq!(
836 InvocationResources {
837 instructions: 10_123_456,
838 mem_bytes: 100_000,
839 disk_read_entries: 30,
840 memory_read_entries: 100,
841 write_entries: 10,
842 disk_read_bytes: 25_600,
843 write_bytes: 10_340,
844 contract_events_size_bytes: 321_654,
845 persistent_rent_ledger_bytes: 1_000_000_000,
846 persistent_entry_rent_bumps: 3,
847 temporary_rent_ledger_bytes: 4_000_000_000,
848 temporary_entry_rent_bumps: 6
849 }
850 .estimate_fees(
851 &FeeConfiguration {
852 fee_per_instruction_increment: 1000,
853 fee_per_disk_read_entry: 2000,
854 fee_per_write_1kb: 3000,
855 fee_per_write_entry: 4000,
856 fee_per_disk_read_1kb: 1500,
857 fee_per_historical_1kb: 300,
858 fee_per_contract_event_1kb: 200,
859 fee_per_transaction_size_1kb: 900,
860 },
861 6000,
862 1000,
863 2000
864 ),
865 FeeEstimate {
866 total: 18_878_354,
868 instructions: 1_012_346,
869 disk_read_entries: 80000,
870 write_entries: 40000,
871 disk_read_bytes: 37500,
872 write_bytes: 30293,
873 contract_events: 62824,
874 persistent_entry_rent: 5871797,
875 temporary_entry_rent: 11743594
876 }
877 );
878
879 assert_eq!(
881 InvocationResources {
882 instructions: i64::MAX,
883 mem_bytes: i64::MAX,
884 disk_read_entries: u32::MAX,
885 memory_read_entries: 100,
886 write_entries: u32::MAX,
887 disk_read_bytes: u32::MAX,
888 write_bytes: u32::MAX,
889 contract_events_size_bytes: u32::MAX,
890 persistent_rent_ledger_bytes: i64::MAX,
891 persistent_entry_rent_bumps: u32::MAX,
892 temporary_rent_ledger_bytes: i64::MAX,
893 temporary_entry_rent_bumps: u32::MAX
894 }
895 .estimate_fees(
896 &FeeConfiguration {
897 fee_per_instruction_increment: i64::MAX,
898 fee_per_disk_read_entry: i64::MAX,
899 fee_per_write_entry: i64::MAX,
900 fee_per_disk_read_1kb: i64::MAX,
901 fee_per_write_1kb: i64::MAX,
902 fee_per_historical_1kb: i64::MAX,
903 fee_per_contract_event_1kb: i64::MAX,
904 fee_per_transaction_size_1kb: i64::MAX,
905 },
906 i64::MAX,
907 i64::MAX,
908 i64::MAX
909 ),
910 FeeEstimate {
911 total: i64::MAX,
912 instructions: 922337203685478,
913 disk_read_entries: i64::MAX,
914 write_entries: i64::MAX,
915 disk_read_bytes: 9007199254740992,
916 write_bytes: 9007199254740992,
917 contract_events: 9007199254740992,
918 persistent_entry_rent: i64::MAX,
919 temporary_entry_rent: i64::MAX
920 }
921 );
922 }
923}