Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RuntimeGenesiConfig: json macro added #5813

Merged
merged 23 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ use sp_core::{crypto::UncheckedInto, sr25519};
use sp_genesis_builder::PresetId;
use testnet_parachains_constants::rococo::{currency::UNITS as ROC, xcm_version::SAFE_XCM_VERSION};

use frame_support::runtime_genesis_config_json;

const ASSET_HUB_ROCOCO_ED: Balance = ExistentialDeposit::get();

fn asset_hub_rococo_genesis(
Expand All @@ -32,7 +34,7 @@ fn asset_hub_rococo_genesis(
endowment: Balance,
id: ParaId,
) -> serde_json::Value {
let config = RuntimeGenesisConfig {
runtime_genesis_config_json!({
balances: BalancesConfig {
balances: endowed_accounts.iter().cloned().map(|k| (k, endowment)).collect(),
},
Expand All @@ -59,10 +61,7 @@ fn asset_hub_rococo_genesis(
safe_xcm_version: Some(SAFE_XCM_VERSION),
..Default::default()
},
..Default::default()
};

serde_json::to_value(config).expect("Could not build genesis config.")
})
}

/// Encapsulates names of predefined presets.
Expand Down
16 changes: 16 additions & 0 deletions prdoc/pr_5813.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
title: "runtime_genesis_config_json macro added"

doc:
- audience: Runtime Dev
description: |
This PR adds a macro that allows to construct a RuntimeGenesisConfig preset
containing only provided fields, while performing the validation of the
entire struct.

Related issue: https://github.com/paritytech/polkadot-sdk/issues/5700

crates:
- name: frame-support
bump: minor
- name: asset-hub-rococo-runtime
bump: patch
79 changes: 79 additions & 0 deletions substrate/frame/support/src/genesis_builder_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,82 @@ where
preset_for_name,
)
}

/// Creates a `RuntimeGenesisConfig` JSON patch.
///
/// This macro creates a default `RuntimeGenesisConfig` initializing provided fields with given
/// values, serialize it to JSON blob, and retain only the specified fields.
///
/// This macro helps to prevent errors that could occur from manually creating JSON objects, such
/// as typos or discrepancies caused by future changes to the `RuntimeGenesisConfig` structure. By
/// using the actual struct, it ensures that the JSON generated is valid and up-to-date.
///
/// This macro assumes that `serde(rename_all = "camelCase")` attribute is used for
/// RuntimeGenesisConfig, what should be the case for frame-based runtimes.
///
/// # Example
///
/// ```rust
/// use frame_support::runtime_genesis_config_json;
/// #[derive(Default, serde::Serialize, serde::Deserialize)]
/// #[serde(rename_all = "camelCase")]
/// struct RuntimeGenesisConfig {
/// a_field: u32,
/// b_field: u32,
/// c_field: u32,
/// }
/// assert_eq!(
/// runtime_genesis_config_json! ({a_field:31, b_field:127}),
/// serde_json::json!({"aField":31, "bField":127})
/// );
/// assert_eq!(
/// runtime_genesis_config_json! ({a_field:31}),
/// serde_json::json!({"aField":31})
/// );
/// ```
#[macro_export]
macro_rules! runtime_genesis_config_json {
michalkucharczyk marked this conversation as resolved.
Show resolved Hide resolved
michalkucharczyk marked this conversation as resolved.
Show resolved Hide resolved
({ $( $key:ident : $value:expr ),* $(,)? }) => {{
let config = RuntimeGenesisConfig {
$( $key : $value, )*
..Default::default()
};

#[inline]
fn compare_keys(
michalkucharczyk marked this conversation as resolved.
Show resolved Hide resolved
mut snake_chars: core::str::Chars,
mut camel_chars: core::str::Chars,
) -> bool {
loop {
match (snake_chars.next(), camel_chars.next()) {
(None, None) => return true,
(None, Some(_)) | (Some(_), None) => return false,
(Some('_'), Some(c)) => {
if let Some(s) = snake_chars.next() {
if s.to_ascii_uppercase() != c {
return false;
};
};
}
(Some(s), Some(c)) => {
if c != s {
return false;
}
}
}
}
}

let mut json_value =
serde_json::to_value(config).expect("serialization to json should work. qed");
if let serde_json::Value::Object(ref mut map) = json_value {
let keys_to_keep : Vec<&'static str> = vec![ $( stringify!($key) ),* ];
map.retain(|json_key, _| {
keys_to_keep.iter().any(|&struct_key| {
compare_keys(struct_key.chars(), json_key.chars())
})
});
}
json_value
}};
}
Loading