soroban_simulation/
network_config.rs1use anyhow::{anyhow, bail, Context, Result};
2use soroban_env_host::budget::Budget;
3use soroban_env_host::fees::{
4 compute_rent_write_fee_per_1kb, FeeConfiguration, RentFeeConfiguration,
5 RentWriteFeeConfiguration,
6};
7use soroban_env_host::storage::SnapshotSource;
8use soroban_env_host::xdr::{
9 ConfigSettingEntry, ConfigSettingId, ContractCostParams, LedgerEntry, LedgerEntryData,
10 LedgerKey, LedgerKeyConfigSetting,
11};
12use soroban_env_host::LedgerInfo;
13use std::rc::Rc;
14
15const CPU_SHADOW_LIMIT_FACTOR: u64 = 10;
16const MEMORY_SHADOW_LIMIT_FACTOR: u64 = 2;
17
18#[derive(Debug, Default, PartialEq, Eq)]
22pub struct NetworkConfig {
23 pub fee_configuration: FeeConfiguration,
24 pub rent_fee_configuration: RentFeeConfiguration,
25 pub tx_max_instructions: i64,
26 pub tx_memory_limit: u32,
27 pub cpu_cost_params: ContractCostParams,
28 pub memory_cost_params: ContractCostParams,
29 pub min_temp_entry_ttl: u32,
31 pub min_persistent_entry_ttl: u32,
32 pub max_entry_ttl: u32,
33}
34
35fn load_configuration_setting(
36 snapshot: &impl SnapshotSource,
37 setting_id: ConfigSettingId,
38) -> Result<ConfigSettingEntry> {
39 let key = Rc::new(LedgerKey::ConfigSetting(LedgerKeyConfigSetting {
40 config_setting_id: setting_id,
41 }));
42 let (entry, _) = snapshot
43 .get(&key)?
44 .ok_or_else(|| anyhow!("setting {setting_id:?} is not present in the snapshot"))?;
45 if let LedgerEntry {
46 data: LedgerEntryData::ConfigSetting(cs),
47 ..
48 } = &*entry
49 {
50 Ok(cs.clone())
51 } else {
52 bail!("encountered unexpected config setting ledger entry {entry:#?}");
53 }
54}
55
56macro_rules! load_setting {
57 ($snapshot:ident, $enum_variant:ident) => {
58 match load_configuration_setting($snapshot, ConfigSettingId::$enum_variant)? {
59 ConfigSettingEntry::$enum_variant(setting) => setting,
60 _ => bail!(
61 "loaded unexpected config setting entry for {:?} key",
62 stringify!($enum_variant)
63 ),
64 }
65 };
66}
67
68fn load_bucket_list_size_from_snapshot(snapshot: &impl SnapshotSource) -> Result<u64> {
69 let window = load_setting!(snapshot, LiveSorobanStateSizeWindow);
70 let mut size_sum = 0_u64;
71 for size in window.iter() {
72 size_sum = size_sum.saturating_add(*size);
75 }
76 if window.is_empty() {
77 bail!("live Soroban state size window is empty");
78 }
79 Ok(size_sum / window.len() as u64)
80}
81
82impl NetworkConfig {
83 pub fn load_from_snapshot(
88 snapshot: &impl SnapshotSource,
89 #[cfg(not(feature = "unstable-next-api"))] _bucket_list_size: u64,
90 ) -> Result<Self> {
91 let compute = load_setting!(snapshot, ContractComputeV0);
92 let ledger_cost = load_setting!(snapshot, ContractLedgerCostV0);
93 let ledger_cost_ext = load_setting!(snapshot, ContractLedgerCostExtV0);
94 let historical_data = load_setting!(snapshot, ContractHistoricalDataV0);
95 let events = load_setting!(snapshot, ContractEventsV0);
96 let bandwidth = load_setting!(snapshot, ContractBandwidthV0);
97 let state_archival = load_setting!(snapshot, StateArchival);
98 let cpu_cost_params = load_setting!(snapshot, ContractCostParamsCpuInstructions);
99 let memory_cost_params = load_setting!(snapshot, ContractCostParamsMemoryBytes);
100
101 let write_fee_configuration = RentWriteFeeConfiguration {
102 state_target_size_bytes: ledger_cost.soroban_state_target_size_bytes,
103 rent_fee_1kb_state_size_low: ledger_cost.rent_fee1_kb_soroban_state_size_low,
104 rent_fee_1kb_state_size_high: ledger_cost.rent_fee1_kb_soroban_state_size_high,
105 state_size_rent_fee_growth_factor: ledger_cost.soroban_state_rent_fee_growth_factor,
106 };
107 let bucket_list_size: i64 = load_bucket_list_size_from_snapshot(snapshot)?
108 .try_into()
109 .context("bucket list size exceeds i64::MAX")?;
110 let rent_fee_per_1kb =
111 compute_rent_write_fee_per_1kb(bucket_list_size, &write_fee_configuration);
112
113 let fee_configuration = FeeConfiguration {
114 fee_per_instruction_increment: compute.fee_rate_per_instructions_increment,
115 fee_per_disk_read_entry: ledger_cost.fee_disk_read_ledger_entry,
116 fee_per_write_entry: ledger_cost.fee_write_ledger_entry,
117 fee_per_disk_read_1kb: ledger_cost.fee_disk_read1_kb,
118 fee_per_write_1kb: ledger_cost_ext.fee_write1_kb,
119 fee_per_historical_1kb: historical_data.fee_historical1_kb,
120 fee_per_contract_event_1kb: events.fee_contract_events1_kb,
121 fee_per_transaction_size_1kb: bandwidth.fee_tx_size1_kb,
122 };
123 let rent_fee_configuration = RentFeeConfiguration {
124 fee_per_rent_1kb: rent_fee_per_1kb,
125 fee_per_write_1kb: ledger_cost_ext.fee_write1_kb,
126 fee_per_write_entry: ledger_cost.fee_write_ledger_entry,
127 persistent_rent_rate_denominator: state_archival.persistent_rent_rate_denominator,
128 temporary_rent_rate_denominator: state_archival.temp_rent_rate_denominator,
129 };
130
131 Ok(Self {
132 fee_configuration,
133 rent_fee_configuration,
134 cpu_cost_params,
135 memory_cost_params,
136 min_temp_entry_ttl: state_archival.min_temporary_ttl,
137 min_persistent_entry_ttl: state_archival.min_persistent_ttl,
138 tx_max_instructions: compute.tx_max_instructions,
139 tx_memory_limit: compute.tx_memory_limit,
140 max_entry_ttl: state_archival.max_entry_ttl,
141 })
142 }
143
144 pub fn fill_config_fields_in_ledger_info(&self, ledger_info: &mut LedgerInfo) {
150 ledger_info.min_persistent_entry_ttl = self.min_persistent_entry_ttl;
151 ledger_info.min_temp_entry_ttl = self.min_temp_entry_ttl;
152 ledger_info.max_entry_ttl = self.max_entry_ttl;
153 }
154
155 pub(crate) fn create_budget(&self) -> Result<Budget> {
156 let cpu_shadow_limit =
157 (self.tx_max_instructions as u64).saturating_mul(CPU_SHADOW_LIMIT_FACTOR);
158 let mem_shadow_limit =
159 (self.tx_memory_limit as u64).saturating_mul(MEMORY_SHADOW_LIMIT_FACTOR);
160 Budget::try_from_configs_with_shadow_limits(
161 self.tx_max_instructions as u64,
162 self.tx_memory_limit as u64,
163 cpu_shadow_limit,
164 mem_shadow_limit,
165 self.cpu_cost_params.clone(),
166 self.memory_cost_params.clone(),
167 )
168 .context("cannot create budget from network configuration")
169 }
170}