1use crate::config::{
2 BandwidthSchedulerConfig, CongestionControlConfig, RuntimeConfig, WitnessConfig,
3};
4use crate::parameter_table::{ParameterTable, ParameterTableDiff};
5use crate::vm;
6use near_primitives_core::types::ProtocolVersion;
7use near_primitives_core::version::{PROTOCOL_VERSION, ProtocolFeature};
8use std::collections::BTreeMap;
9use std::ops::Bound;
10use std::sync::Arc;
11
12macro_rules! include_config {
13 ($file:expr) => {
14 include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/res/runtime_configs/", $file))
15 };
16}
17
18static BASE_CONFIG: &str = include_config!("parameters.yaml");
21
22static CONFIG_DIFFS: &[(ProtocolVersion, &str)] = &[
25 (35, include_config!("35.yaml")),
26 (42, include_config!("42.yaml")),
27 (46, include_config!("46.yaml")),
28 (48, include_config!("48.yaml")),
29 (49, include_config!("49.yaml")),
30 (50, include_config!("50.yaml")),
31 (52, include_config!("52.yaml")),
33 (53, include_config!("53.yaml")),
36 (55, include_config!("55.yaml")),
37 (57, include_config!("57.yaml")),
38 (59, include_config!("59.yaml")),
40 (61, include_config!("61.yaml")),
41 (62, include_config!("62.yaml")),
42 (63, include_config!("63.yaml")),
43 (64, include_config!("64.yaml")),
44 (66, include_config!("66.yaml")),
45 (67, include_config!("67.yaml")),
46 (68, include_config!("68.yaml")),
48 (69, include_config!("69.yaml")),
50 (70, include_config!("70.yaml")),
52 (72, include_config!("72.yaml")),
54 (73, include_config!("73.yaml")),
56 (74, include_config!("74.yaml")),
57 (77, include_config!("77.yaml")),
58 (78, include_config!("78.yaml")),
59 (79, include_config!("79.yaml")),
60 (129, include_config!("129.yaml")),
61];
62
63pub static INITIAL_TESTNET_CONFIG: &str = include_config!("parameters_testnet.yaml");
65
66#[derive(Clone, Debug)]
68pub struct RuntimeConfigStore {
69 store: BTreeMap<ProtocolVersion, Arc<RuntimeConfig>>,
71}
72
73impl RuntimeConfigStore {
74 pub fn new(genesis_runtime_config: Option<&RuntimeConfig>) -> Self {
85 let mut params: ParameterTable =
86 BASE_CONFIG.parse().expect("Failed parsing base parameter file.");
87
88 let mut store = BTreeMap::new();
89 #[cfg(not(feature = "calimero_zero_storage"))]
90 {
91 let initial_config = RuntimeConfig::new(¶ms).unwrap_or_else(|err| {
92 panic!(
93 "Failed generating `RuntimeConfig` from parameters for base parameter file. \
94 Error: {err:?}"
95 )
96 });
97 store.insert(0, Arc::new(initial_config));
98 }
99 #[cfg(feature = "calimero_zero_storage")]
100 {
101 let mut initial_config = RuntimeConfig::new(¶ms).unwrap_or_else(|err| {
102 panic!(
103 "Failed generating `RuntimeConfig` from parameters for base parameter file. \
104 Error: {err:?}"
105 )
106 });
107 let fees = Arc::make_mut(&mut initial_config.fees);
108 fees.storage_usage_config.storage_amount_per_byte = 0;
109 store.insert(0, Arc::new(initial_config));
110 }
111
112 for (protocol_version, diff_bytes) in CONFIG_DIFFS {
113 let diff: ParameterTableDiff = diff_bytes.parse().unwrap_or_else(|err| {
114 panic!(
115 "Failed parsing runtime parameters diff for version {protocol_version}. \
116 Error: {err:?}"
117 )
118 });
119 params.apply_diff(diff).unwrap_or_else(|err| {
120 panic!(
121 "Failed applying diff to `RuntimeConfig` for version {protocol_version}. \
122 Error: {err}"
123 )
124 });
125 #[cfg(not(feature = "calimero_zero_storage"))]
126 store.insert(
127 *protocol_version,
128 Arc::new(RuntimeConfig::new(¶ms).unwrap_or_else(|err| {
129 panic!(
130 "Failed generating `RuntimeConfig` from parameters for \
131 version {protocol_version}. Error: {err:?}"
132 )
133 })),
134 );
135 #[cfg(feature = "calimero_zero_storage")]
136 {
137 let mut runtime_config = RuntimeConfig::new(¶ms).unwrap_or_else(|err| {
138 panic!(
139 "Failed generating `RuntimeConfig` from parameters for \
140 version {protocol_version}. Error: {err:?}"
141 )
142 });
143 let fees = Arc::make_mut(&mut runtime_config.fees);
144 fees.storage_usage_config.storage_amount_per_byte = 0;
145 store.insert(*protocol_version, Arc::new(runtime_config));
146 }
147 }
148
149 if let Some(runtime_config) = genesis_runtime_config {
150 let mut fees = crate::RuntimeFeesConfig::clone(&runtime_config.fees);
151 fees.storage_usage_config.storage_amount_per_byte = 10u128.pow(19);
152 store.insert(
153 42,
154 Arc::new(RuntimeConfig {
155 fees: Arc::new(fees),
156 wasm_config: Arc::clone(&runtime_config.wasm_config),
157 account_creation_config: runtime_config.account_creation_config.clone(),
158 congestion_control_config: runtime_config.congestion_control_config,
159 witness_config: runtime_config.witness_config,
160 bandwidth_scheduler_config: runtime_config.bandwidth_scheduler_config,
161 use_state_stored_receipt: runtime_config.use_state_stored_receipt,
162 }),
163 );
164 store.insert(0, Arc::new(runtime_config.clone()));
165 }
166
167 Self { store }
168 }
169
170 pub fn for_chain_id(chain_id: &str) -> Self {
179 match chain_id {
180 near_primitives_core::chains::TESTNET => {
181 let genesis_runtime_config = RuntimeConfig::initial_testnet_config();
182 Self::new(Some(&genesis_runtime_config))
183 }
184 near_primitives_core::chains::BENCHMARKNET => {
185 let mut config_store = Self::new(None);
186 let mut config = RuntimeConfig::clone(config_store.get_config(PROTOCOL_VERSION));
187 config.congestion_control_config = CongestionControlConfig::test_disabled();
188 config.bandwidth_scheduler_config = BandwidthSchedulerConfig::test_disabled();
189 config.witness_config = WitnessConfig::test_disabled();
190 let mut wasm_config = vm::Config::clone(&config.wasm_config);
191 wasm_config.limit_config.per_receipt_storage_proof_size_limit = usize::max_value();
192 config.wasm_config = Arc::new(wasm_config);
193 config_store.store.insert(PROTOCOL_VERSION, Arc::new(config));
194 config_store
195 }
196 near_primitives_core::chains::CONGESTION_CONTROL_TEST => {
197 let mut config_store = Self::new(None);
198
199 #[allow(deprecated)]
202 let source_protocol_version =
203 ProtocolFeature::_DeprecatedCongestionControl.protocol_version();
204 let source_runtime_config = config_store.get_config(source_protocol_version);
205
206 let mut config = RuntimeConfig::clone(config_store.get_config(PROTOCOL_VERSION));
207 config.congestion_control_config = source_runtime_config.congestion_control_config;
208
209 config_store.store.insert(PROTOCOL_VERSION, Arc::new(config));
210 config_store
211 }
212 _ => Self::new(None),
213 }
214 }
215
216 pub fn with_one_config(runtime_config: RuntimeConfig) -> Self {
218 Self { store: BTreeMap::from_iter([(0, Arc::new(runtime_config))].iter().cloned()) }
219 }
220
221 pub fn new_custom(store: BTreeMap<ProtocolVersion, Arc<RuntimeConfig>>) -> Self {
223 Self { store }
224 }
225
226 pub fn test() -> Self {
228 Self::with_one_config(RuntimeConfig::test())
229 }
230
231 pub fn test_congestion_control_disabled() -> Self {
233 let mut config = RuntimeConfig::test();
234 config.congestion_control_config = CongestionControlConfig::test_disabled();
235
236 Self::with_one_config(config)
237 }
238
239 pub fn free() -> Self {
241 Self::with_one_config(RuntimeConfig::free())
242 }
243
244 pub fn get_config(&self, protocol_version: ProtocolVersion) -> &Arc<RuntimeConfig> {
246 self.store
247 .range((Bound::Unbounded, Bound::Included(protocol_version)))
248 .next_back()
249 .unwrap_or_else(|| {
250 panic!("Not found RuntimeConfig for protocol version {}", protocol_version)
251 })
252 .1
253 }
254}
255
256#[cfg(test)]
257mod tests {
258 use super::*;
259 use crate::cost::ActionCosts;
260 use std::collections::HashSet;
261
262 const GENESIS_PROTOCOL_VERSION: ProtocolVersion = 29;
263
264 #[test]
265 fn all_configs_are_specified() {
266 let file_versions =
267 std::fs::read_dir(concat!(env!("CARGO_MANIFEST_DIR"), "/res/runtime_configs/"))
268 .expect("can open config directory");
269 let mut files = file_versions
270 .into_iter()
271 .map(|de| {
272 de.expect("dir entry should read successfully")
273 .path()
274 .file_name()
275 .expect("dir entry should have a filename")
276 .to_string_lossy()
277 .into_owned()
278 })
279 .collect::<HashSet<_>>();
280
281 for (ver, _) in super::CONFIG_DIFFS {
282 assert!(files.remove(&format!("{ver}.yaml")), "{ver}.yaml file is missing?");
283 }
284
285 for file in files {
286 let Some((name, "yaml")) = file.rsplit_once(".") else { continue };
287 let Ok(version_num) = name.parse::<u32>() else { continue };
288 panic!("CONFIG_DIFFS does not contain reference to the {version_num}.yaml file!");
289 }
290 }
291
292 #[test]
293 fn test_override_account_length() {
294 let base_store = RuntimeConfigStore::new(None);
296 let base_cfg = base_store.get_config(GENESIS_PROTOCOL_VERSION);
297 assert_eq!(base_cfg.account_creation_config.min_allowed_top_level_account_length, 32);
298
299 let mut cfg = base_cfg.as_ref().clone();
300 cfg.account_creation_config.min_allowed_top_level_account_length = 0;
301
302 let new_store = RuntimeConfigStore::new(Some(&cfg));
304 let new_cfg = new_store.get_config(GENESIS_PROTOCOL_VERSION);
305 assert_eq!(new_cfg.account_creation_config.min_allowed_top_level_account_length, 0);
306 }
307
308 #[test]
309 fn test_parameter_merging() {
310 let mut base_params: ParameterTable = BASE_CONFIG.parse().unwrap();
311 let base_config = RuntimeConfig::new(&base_params).unwrap();
312
313 let mock_diff_str = r#"
314 max_length_storage_key: { old: 4_194_304, new: 42 }
315 action_receipt_creation: {
316 old: {
317 send_sir: 108_059_500_000,
318 send_not_sir: 108_059_500_000,
319 execution: 108_059_500_000,
320 },
321 new: {
322 send_sir: 100000000,
323 send_not_sir: 108_059_500_000,
324 execution: 108_059_500_000,
325 },
326 }
327 "#;
328
329 let mock_diff: ParameterTableDiff = mock_diff_str.parse().unwrap();
330
331 base_params.apply_diff(mock_diff).unwrap();
332 let modified_config = RuntimeConfig::new(&base_params).unwrap();
333
334 assert_eq!(modified_config.wasm_config.limit_config.max_length_storage_key, 42);
335 assert_eq!(modified_config.fees.fee(ActionCosts::new_action_receipt).send_sir, 100000000);
336
337 assert_eq!(
338 base_config.storage_amount_per_byte(),
339 modified_config.storage_amount_per_byte()
340 );
341 }
342
343 #[test]
352 #[cfg(not(feature = "nightly"))]
353 #[cfg(not(feature = "calimero_zero_storage"))]
354 fn test_json_unchanged() {
355 use crate::view::RuntimeConfigView;
356 use near_primitives_core::version::PROTOCOL_VERSION;
357
358 let store = RuntimeConfigStore::new(None);
359 let mut any_failure = false;
360
361 for version in store.store.keys() {
362 let snapshot_name = format!("{version}.json");
363 let config_view = RuntimeConfigView::from(store.get_config(*version).as_ref().clone());
364 any_failure |= std::panic::catch_unwind(|| {
365 insta::assert_json_snapshot!(snapshot_name, config_view, { ".wasm_config.vm_kind" => "<REDACTED>"});
366 })
367 .is_err();
368 }
369
370 {
372 let mut params: ParameterTable = BASE_CONFIG.parse().unwrap();
373 for (_, diff_bytes) in
374 CONFIG_DIFFS.iter().filter(|(version, _)| *version <= PROTOCOL_VERSION)
375 {
376 params.apply_diff(diff_bytes.parse().unwrap()).unwrap();
377 }
378 insta::with_settings!({
379 snapshot_path => "../res/runtime_configs",
380 prepend_module_to_snapshot => false,
381 description => "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.",
382 omit_expression => true,
383 }, {
384 any_failure |= std::panic::catch_unwind(|| {
385 insta::assert_snapshot!("parameters", params);
386 }).is_err();
387 });
388 }
389
390 let params = INITIAL_TESTNET_CONFIG.parse().unwrap();
392 let new_genesis_runtime_config = RuntimeConfig::new(¶ms).unwrap();
393 let testnet_store = RuntimeConfigStore::new(Some(&new_genesis_runtime_config));
394
395 for version in testnet_store.store.keys() {
396 let snapshot_name = format!("testnet_{version}.json");
397 let config_view = RuntimeConfigView::from(store.get_config(*version).as_ref().clone());
398 any_failure |= std::panic::catch_unwind(|| {
399 insta::assert_json_snapshot!(snapshot_name, config_view, { ".wasm_config.vm_kind" => "<REDACTED>"});
400 })
401 .is_err();
402 }
403 if any_failure {
404 panic!("some snapshot assertions failed");
405 }
406 }
407
408 #[test]
409 #[cfg(feature = "calimero_zero_storage")]
410 fn test_calimero_storage_costs_zero() {
411 let store = RuntimeConfigStore::new(None);
412 for (_, config) in &store.store {
413 assert_eq!(config.storage_amount_per_byte(), 0u128);
414 }
415 }
416
417 #[test]
418 fn test_benchmarknet_config() {
419 let store = RuntimeConfigStore::for_chain_id(near_primitives_core::chains::BENCHMARKNET);
420 let config = store.get_config(PROTOCOL_VERSION);
421 assert_eq!(config.witness_config.main_storage_proof_size_soft_limit, usize::MAX);
422 }
423}