1use crate::config::{CongestionControlConfig, RuntimeConfig, WitnessConfig};
2use crate::parameter_table::{ParameterTable, ParameterTableDiff};
3use crate::vm;
4use near_primitives_core::types::ProtocolVersion;
5use near_primitives_core::version::{ProtocolFeature, PROTOCOL_VERSION};
6use std::collections::BTreeMap;
7use std::ops::Bound;
8use std::sync::Arc;
9
10macro_rules! include_config {
11 ($file:expr) => {
12 include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/res/runtime_configs/", $file))
13 };
14}
15
16static BASE_CONFIG: &str = include_config!("parameters.yaml");
19
20static CONFIG_DIFFS: &[(ProtocolVersion, &str)] = &[
23 (35, include_config!("35.yaml")),
24 (42, include_config!("42.yaml")),
25 (46, include_config!("46.yaml")),
26 (48, include_config!("48.yaml")),
27 (49, include_config!("49.yaml")),
28 (50, include_config!("50.yaml")),
29 (52, include_config!("52.yaml")),
31 (53, include_config!("53.yaml")),
34 (55, include_config!("55.yaml")),
35 (57, include_config!("57.yaml")),
36 (59, include_config!("59.yaml")),
38 (61, include_config!("61.yaml")),
39 (62, include_config!("62.yaml")),
40 (63, include_config!("63.yaml")),
41 (64, include_config!("64.yaml")),
42 (66, include_config!("66.yaml")),
43 (67, include_config!("67.yaml")),
44 (68, include_config!("68.yaml")),
46 (69, include_config!("69.yaml")),
48 (70, include_config!("70.yaml")),
50 (72, include_config!("72.yaml")),
52 (73, include_config!("73.yaml")),
54 (74, include_config!("74.yaml")),
55 (129, include_config!("129.yaml")),
56];
57
58pub static INITIAL_TESTNET_CONFIG: &str = include_config!("parameters_testnet.yaml");
60
61#[derive(Clone, Debug)]
63pub struct RuntimeConfigStore {
64 store: BTreeMap<ProtocolVersion, Arc<RuntimeConfig>>,
66}
67
68impl RuntimeConfigStore {
69 pub fn new(genesis_runtime_config: Option<&RuntimeConfig>) -> Self {
80 let mut params: ParameterTable =
81 BASE_CONFIG.parse().expect("Failed parsing base parameter file.");
82
83 let mut store = BTreeMap::new();
84 #[cfg(not(feature = "calimero_zero_storage"))]
85 {
86 let initial_config = RuntimeConfig::new(¶ms).unwrap_or_else(|err| panic!("Failed generating `RuntimeConfig` from parameters for base parameter file. Error: {err}"));
87 store.insert(0, Arc::new(initial_config));
88 }
89 #[cfg(feature = "calimero_zero_storage")]
90 {
91 let mut initial_config = RuntimeConfig::new(¶ms).unwrap_or_else(|err| panic!("Failed generating `RuntimeConfig` from parameters for base parameter file. Error: {err}"));
92 let fees = Arc::make_mut(&mut initial_config.fees);
93 fees.storage_usage_config.storage_amount_per_byte = 0;
94 store.insert(0, Arc::new(initial_config));
95 }
96
97 for (protocol_version, diff_bytes) in CONFIG_DIFFS {
98 let diff :ParameterTableDiff = diff_bytes.parse().unwrap_or_else(|err| panic!("Failed parsing runtime parameters diff for version {protocol_version}. Error: {err}"));
99 params.apply_diff(diff).unwrap_or_else(|err| panic!("Failed applying diff to `RuntimeConfig` for version {protocol_version}. Error: {err}"));
100 #[cfg(not(feature = "calimero_zero_storage"))]
101 store.insert(
102 *protocol_version,
103 Arc::new(RuntimeConfig::new(¶ms).unwrap_or_else(|err| panic!("Failed generating `RuntimeConfig` from parameters for version {protocol_version}. Error: {err}"))),
104 );
105 #[cfg(feature = "calimero_zero_storage")]
106 {
107 let mut runtime_config = RuntimeConfig::new(¶ms).unwrap_or_else(|err| panic!("Failed generating `RuntimeConfig` from parameters for version {protocol_version}. Error: {err}"));
108 let fees = Arc::make_mut(&mut runtime_config.fees);
109 fees.storage_usage_config.storage_amount_per_byte = 0;
110 store.insert(*protocol_version, Arc::new(runtime_config));
111 }
112 }
113
114 if let Some(runtime_config) = genesis_runtime_config {
115 let mut fees = crate::RuntimeFeesConfig::clone(&runtime_config.fees);
116 fees.storage_usage_config.storage_amount_per_byte = 10u128.pow(19);
117 store.insert(
118 42,
119 Arc::new(RuntimeConfig {
120 fees: Arc::new(fees),
121 wasm_config: Arc::clone(&runtime_config.wasm_config),
122 account_creation_config: runtime_config.account_creation_config.clone(),
123 congestion_control_config: runtime_config.congestion_control_config,
124 witness_config: runtime_config.witness_config,
125 bandwidth_scheduler_config: runtime_config.bandwidth_scheduler_config,
126 use_state_stored_receipt: runtime_config.use_state_stored_receipt,
127 }),
128 );
129 store.insert(0, Arc::new(runtime_config.clone()));
130 }
131
132 Self { store }
133 }
134
135 pub fn for_chain_id(chain_id: &str) -> Self {
144 match chain_id {
145 near_primitives_core::chains::TESTNET => {
146 let genesis_runtime_config = RuntimeConfig::initial_testnet_config();
147 Self::new(Some(&genesis_runtime_config))
148 }
149 near_primitives_core::chains::BENCHMARKNET => {
150 let mut config_store = Self::new(None);
151 let mut config = RuntimeConfig::clone(config_store.get_config(PROTOCOL_VERSION));
152 config.congestion_control_config = CongestionControlConfig::test_disabled();
153 config.witness_config = WitnessConfig::test_disabled();
154 let mut wasm_config = vm::Config::clone(&config.wasm_config);
155 wasm_config.limit_config.per_receipt_storage_proof_size_limit = 999_999_999_999_999;
156 config.wasm_config = Arc::new(wasm_config);
157 config_store.store.insert(PROTOCOL_VERSION, Arc::new(config));
158 config_store
159 }
160 near_primitives_core::chains::CONGESTION_CONTROL_TEST => {
161 let mut config_store = Self::new(None);
162
163 let source_protocol_version = ProtocolFeature::CongestionControl.protocol_version();
166 let source_runtime_config = config_store.get_config(source_protocol_version);
167
168 let mut config = RuntimeConfig::clone(config_store.get_config(PROTOCOL_VERSION));
169 config.congestion_control_config = source_runtime_config.congestion_control_config;
170
171 config_store.store.insert(PROTOCOL_VERSION, Arc::new(config));
172 config_store
173 }
174 _ => Self::new(None),
175 }
176 }
177
178 pub fn with_one_config(runtime_config: RuntimeConfig) -> Self {
180 Self { store: BTreeMap::from_iter([(0, Arc::new(runtime_config))].iter().cloned()) }
181 }
182
183 pub fn new_custom(store: BTreeMap<ProtocolVersion, Arc<RuntimeConfig>>) -> Self {
185 Self { store }
186 }
187
188 pub fn test() -> Self {
190 Self::with_one_config(RuntimeConfig::test())
191 }
192
193 pub fn test_congestion_control_disabled() -> Self {
195 let mut config = RuntimeConfig::test();
196 config.congestion_control_config = CongestionControlConfig::test_disabled();
197
198 Self::with_one_config(config)
199 }
200
201 pub fn free() -> Self {
203 Self::with_one_config(RuntimeConfig::free())
204 }
205
206 pub fn get_config(&self, protocol_version: ProtocolVersion) -> &Arc<RuntimeConfig> {
208 self.store
209 .range((Bound::Unbounded, Bound::Included(protocol_version)))
210 .next_back()
211 .unwrap_or_else(|| {
212 panic!("Not found RuntimeConfig for protocol version {}", protocol_version)
213 })
214 .1
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221 use crate::cost::{ActionCosts, ExtCosts};
222 use near_primitives_core::version::ProtocolFeature::{
223 DecreaseFunctionCallBaseCost, LowerDataReceiptAndEcrecoverBaseCost, LowerStorageCost,
224 LowerStorageKeyLimit,
225 };
226 use std::collections::HashSet;
227
228 const GENESIS_PROTOCOL_VERSION: ProtocolVersion = 29;
229 const RECEIPTS_DEPTH: u64 = 63;
230
231 #[test]
232 fn all_configs_are_specified() {
233 let file_versions =
234 std::fs::read_dir(concat!(env!("CARGO_MANIFEST_DIR"), "/res/runtime_configs/"))
235 .expect("can open config directory");
236 let mut files = file_versions
237 .into_iter()
238 .map(|de| {
239 de.expect("dir entry should read successfully")
240 .path()
241 .file_name()
242 .expect("dir entry should have a filename")
243 .to_string_lossy()
244 .into_owned()
245 })
246 .collect::<HashSet<_>>();
247
248 for (ver, _) in super::CONFIG_DIFFS {
249 assert!(files.remove(&format!("{ver}.yaml")), "{ver}.yaml file is missing?");
250 }
251
252 for file in files {
253 let Some((name, "yaml")) = file.rsplit_once(".") else { continue };
254 let Ok(version_num) = name.parse::<u32>() else { continue };
255 panic!("CONFIG_DIFFS does not contain reference to the {version_num}.yaml file!");
256 }
257 }
258
259 #[test]
260 fn test_max_prepaid_gas() {
261 let store = RuntimeConfigStore::new(None);
262 for (protocol_version, config) in store.store.iter() {
263 if *protocol_version >= DecreaseFunctionCallBaseCost.protocol_version() {
264 continue;
265 }
266
267 assert!(
269 config.wasm_config.limit_config.max_total_prepaid_gas
270 / config.fees.min_receipt_with_function_call_gas()
271 <= 63,
272 "The maximum desired depth of receipts for protocol version {} should be at most {}",
273 protocol_version,
274 RECEIPTS_DEPTH
275 );
276 }
277 }
278
279 #[test]
280 #[cfg(not(feature = "calimero_zero_storage"))]
281 fn test_lower_storage_cost() {
282 let store = RuntimeConfigStore::new(None);
283 let base_cfg = store.get_config(GENESIS_PROTOCOL_VERSION);
284 let new_cfg = store.get_config(LowerStorageCost.protocol_version());
285 assert!(base_cfg.storage_amount_per_byte() > new_cfg.storage_amount_per_byte());
286 }
287
288 #[test]
289 fn test_override_account_length() {
290 let base_store = RuntimeConfigStore::new(None);
292 let base_cfg = base_store.get_config(GENESIS_PROTOCOL_VERSION);
293 assert_eq!(base_cfg.account_creation_config.min_allowed_top_level_account_length, 32);
294
295 let mut cfg = base_cfg.as_ref().clone();
296 cfg.account_creation_config.min_allowed_top_level_account_length = 0;
297
298 let new_store = RuntimeConfigStore::new(Some(&cfg));
300 let new_cfg = new_store.get_config(GENESIS_PROTOCOL_VERSION);
301 assert_eq!(new_cfg.account_creation_config.min_allowed_top_level_account_length, 0);
302 }
303
304 #[test]
305 fn test_lower_data_receipt_cost() {
306 let store = RuntimeConfigStore::new(None);
307 let base_cfg = store.get_config(LowerStorageCost.protocol_version());
308 let new_cfg = store.get_config(LowerDataReceiptAndEcrecoverBaseCost.protocol_version());
309 assert!(
310 base_cfg.fees.fee(ActionCosts::new_data_receipt_base).send_sir
311 > new_cfg.fees.fee(ActionCosts::new_data_receipt_base).send_sir
312 );
313 assert!(
314 base_cfg.fees.fee(ActionCosts::new_data_receipt_byte).send_sir
315 > new_cfg.fees.fee(ActionCosts::new_data_receipt_byte).send_sir
316 );
317 }
318
319 #[test]
322 #[cfg(not(feature = "calimero_zero_storage"))]
323 fn test_override_runtime_config() {
324 let store = RuntimeConfigStore::new(None);
325 let config = store.get_config(0);
326
327 let mut base_params = BASE_CONFIG.parse().unwrap();
328 let base_config = RuntimeConfig::new(&base_params).unwrap();
329 assert_eq!(config.as_ref(), &base_config);
330
331 let config = store.get_config(LowerStorageCost.protocol_version());
332 assert_eq!(base_config.storage_amount_per_byte(), 100_000_000_000_000_000_000u128);
333 assert_eq!(config.storage_amount_per_byte(), 10_000_000_000_000_000_000u128);
334 assert_eq!(config.fees.fee(ActionCosts::new_data_receipt_base).send_sir, 4_697_339_419_375);
335 assert_ne!(config.as_ref(), &base_config);
336 assert_ne!(
337 config.as_ref(),
338 store.get_config(LowerStorageCost.protocol_version() - 1).as_ref()
339 );
340
341 for (ver, diff) in &CONFIG_DIFFS[..] {
342 if *ver <= LowerStorageCost.protocol_version() {
343 base_params.apply_diff(diff.parse().unwrap()).unwrap();
344 }
345 }
346 let expected_config = RuntimeConfig::new(&base_params).unwrap();
347 assert_eq!(**config, expected_config);
348
349 let config = store.get_config(LowerDataReceiptAndEcrecoverBaseCost.protocol_version());
350 assert_eq!(config.fees.fee(ActionCosts::new_data_receipt_base).send_sir, 36_486_732_312);
351 for (ver, diff) in &CONFIG_DIFFS[..] {
352 if *ver <= LowerStorageCost.protocol_version() {
353 continue;
354 } else if *ver <= LowerDataReceiptAndEcrecoverBaseCost.protocol_version() {
355 base_params.apply_diff(diff.parse().unwrap()).unwrap();
356 }
357 }
358 let expected_config = RuntimeConfig::new(&base_params).unwrap();
359 assert_eq!(config.as_ref(), &expected_config);
360 }
361
362 #[test]
363 fn test_lower_ecrecover_base_cost() {
364 let store = RuntimeConfigStore::new(None);
365 let base_cfg = store.get_config(LowerStorageCost.protocol_version());
366 let new_cfg = store.get_config(LowerDataReceiptAndEcrecoverBaseCost.protocol_version());
367 assert!(
368 base_cfg.wasm_config.ext_costs.gas_cost(ExtCosts::ecrecover_base)
369 > new_cfg.wasm_config.ext_costs.gas_cost(ExtCosts::ecrecover_base)
370 );
371 }
372
373 #[test]
374 fn test_lower_max_length_storage_key() {
375 let store = RuntimeConfigStore::new(None);
376 let base_cfg = store.get_config(LowerStorageKeyLimit.protocol_version() - 1);
377 let new_cfg = store.get_config(LowerStorageKeyLimit.protocol_version());
378 assert!(
379 base_cfg.wasm_config.limit_config.max_length_storage_key
380 > new_cfg.wasm_config.limit_config.max_length_storage_key
381 );
382 }
383
384 #[test]
393 #[cfg(not(feature = "nightly"))]
394 #[cfg(not(feature = "calimero_zero_storage"))]
395 fn test_json_unchanged() {
396 use crate::view::RuntimeConfigView;
397 use near_primitives_core::version::PROTOCOL_VERSION;
398
399 let store = RuntimeConfigStore::new(None);
400 let mut any_failure = false;
401
402 for version in store.store.keys() {
403 let snapshot_name = format!("{version}.json");
404 let config_view = RuntimeConfigView::from(store.get_config(*version).as_ref().clone());
405 any_failure |= std::panic::catch_unwind(|| {
406 insta::assert_json_snapshot!(snapshot_name, config_view, { ".wasm_config.vm_kind" => "<REDACTED>"});
407 })
408 .is_err();
409 }
410
411 {
413 let mut params: ParameterTable = BASE_CONFIG.parse().unwrap();
414 for (_, diff_bytes) in
415 CONFIG_DIFFS.iter().filter(|(version, _)| *version <= PROTOCOL_VERSION)
416 {
417 params.apply_diff(diff_bytes.parse().unwrap()).unwrap();
418 }
419 insta::with_settings!({
420 snapshot_path => "../res/runtime_configs",
421 prepend_module_to_snapshot => false,
422 description => "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
423 omit_expression => true,
424 }, {
425 any_failure |= std::panic::catch_unwind(|| {
426 insta::assert_snapshot!("parameters", params);
427 }).is_err();
428 });
429 }
430
431 let params = INITIAL_TESTNET_CONFIG.parse().unwrap();
433 let new_genesis_runtime_config = RuntimeConfig::new(¶ms).unwrap();
434 let testnet_store = RuntimeConfigStore::new(Some(&new_genesis_runtime_config));
435
436 for version in testnet_store.store.keys() {
437 let snapshot_name = format!("testnet_{version}.json");
438 let config_view = RuntimeConfigView::from(store.get_config(*version).as_ref().clone());
439 any_failure |= std::panic::catch_unwind(|| {
440 insta::assert_json_snapshot!(snapshot_name, config_view, { ".wasm_config.vm_kind" => "<REDACTED>"});
441 })
442 .is_err();
443 }
444 if any_failure {
445 panic!("some snapshot assertions failed");
446 }
447 }
448
449 #[test]
450 #[cfg(feature = "calimero_zero_storage")]
451 fn test_calimero_storage_costs_zero() {
452 let store = RuntimeConfigStore::new(None);
453 for (_, config) in store.store.iter() {
454 assert_eq!(config.storage_amount_per_byte(), 0u128);
455 }
456 }
457
458 #[test]
459 fn test_benchmarknet_config() {
460 let store = RuntimeConfigStore::for_chain_id(near_primitives_core::chains::BENCHMARKNET);
461 let config = store.get_config(PROTOCOL_VERSION);
462 assert_eq!(config.witness_config.main_storage_proof_size_soft_limit, usize::MAX);
463 }
464}