Skip to content

Commit

Permalink
RuntimeGenesiConfig: json macro added (#5813)
Browse files Browse the repository at this point in the history
  • Loading branch information
michalkucharczyk and skunert authored Oct 25, 2024
1 parent 5d7181c commit 7e99621
Show file tree
Hide file tree
Showing 12 changed files with 1,044 additions and 55 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use crate::*;
use alloc::{vec, vec::Vec};
use cumulus_primitives_core::ParaId;
use frame_support::build_struct_json_patch;
use hex_literal::hex;
use parachains_common::{AccountId, AuraId};
use sp_core::crypto::UncheckedInto;
Expand All @@ -33,15 +34,14 @@ fn asset_hub_rococo_genesis(
endowment: Balance,
id: ParaId,
) -> serde_json::Value {
let config = RuntimeGenesisConfig {
build_struct_json_patch!(RuntimeGenesisConfig {
balances: BalancesConfig {
balances: endowed_accounts.iter().cloned().map(|k| (k, endowment)).collect(),
},
parachain_info: ParachainInfoConfig { parachain_id: id, ..Default::default() },
parachain_info: ParachainInfoConfig { parachain_id: id },
collator_selection: CollatorSelectionConfig {
invulnerables: invulnerables.iter().cloned().map(|(acc, _)| acc).collect(),
candidacy_bond: ASSET_HUB_ROCOCO_ED * 16,
..Default::default()
},
session: SessionConfig {
keys: invulnerables
Expand All @@ -54,16 +54,9 @@ fn asset_hub_rococo_genesis(
)
})
.collect(),
..Default::default()
},
polkadot_xcm: PolkadotXcmConfig {
safe_xcm_version: Some(SAFE_XCM_VERSION),
..Default::default()
},
..Default::default()
};

serde_json::to_value(config).expect("Could not build genesis config.")
polkadot_xcm: PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION) },
})
}

/// Encapsulates names of predefined presets.
Expand Down
22 changes: 16 additions & 6 deletions docs/sdk/src/reference_docs/chain_spec_genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,22 @@
//! others useful for testing.
//!
//! Internally, presets can be provided in a number of ways:
//! - JSON in string form:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_1)]
//! - JSON using runtime types to serialize values:
//! - using [`build_struct_json_patch`] macro (**recommended**):
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_2)]
//! - JSON using runtime types to serialize values:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_3)]
//! - JSON in string form:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_1)]
//!
//! It is worth noting that a preset does not have to be the full `RuntimeGenesisConfig`, in that
//! sense that it does not have to contain all the keys of the struct. The preset is actually a JSON
//! patch that will be merged with the default value of `RuntimeGenesisConfig`. This approach should
//! simplify maintenance of built-in presets. The following example illustrates a runtime genesis
//! config patch:
//! config patch with a single key built using [`build_struct_json_patch`] macro:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_4)]
//! This results in the following JSON blob:
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/tests/chain_spec_builder_tests.rs", preset_4_json)]
//!
//!
//! ## Note on the importance of testing presets
//!
Expand All @@ -122,15 +127,19 @@
//!
//! ## Note on the importance of using the `deny_unknown_fields` attribute
//!
//! It is worth noting that it is easy to make a hard-to-spot mistake, as in the following example
//! ([`FooStruct`] does not contain `fieldC`):
//! It is worth noting that when manually building preset JSON blobs it is easy to make a
//! hard-to-spot mistake, as in the following example ([`FooStruct`] does not contain `fieldC`):
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", preset_invalid)]
//! Even though `preset_invalid` contains a key that does not exist, the deserialization of the JSON
//! blob does not fail. The misspelling is silently ignored due to the lack of the
//! [`deny_unknown_fields`] attribute on the [`FooStruct`] struct, which is internally used in
//! `GenesisConfig`.
#![doc = docify::embed!("./src/reference_docs/chain_spec_runtime/src/presets.rs", invalid_preset_works)]
//!
//! To avoid this problem [`build_struct_json_patch`] macro shall be used whenever possible (it
//! internally instantiates the struct before serializang it JSON blob, so all unknown fields shall
//! be caught at compilation time).
//!
//! ## Runtime `GenesisConfig` raw format
//!
//! A raw format of genesis config contains just the state's keys and values as they are stored in
Expand Down Expand Up @@ -182,6 +191,7 @@
//! [`get_preset`]: frame_support::genesis_builder_helper::get_preset
//! [`pallet::genesis_build`]: frame_support::pallet_macros::genesis_build
//! [`pallet::genesis_config`]: frame_support::pallet_macros::genesis_config
//! [`build_struct_json_patch`]: frame_support::build_struct_json_patch
//! [`BuildGenesisConfig`]: frame_support::traits::BuildGenesisConfig
//! [`serde`]: https://serde.rs/field-attrs.html
//! [`get_storage_for_patch`]: sc_chain_spec::GenesisConfigBuilderRuntimeCaller::get_storage_for_patch
Expand Down
2 changes: 2 additions & 0 deletions docs/sdk/src/reference_docs/chain_spec_runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ publish = false
[dependencies]
docify = { workspace = true }
codec = { workspace = true }
frame-support = { workspace = true }
scale-info = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
Expand Down Expand Up @@ -49,6 +50,7 @@ std = [
"codec/std",
"scale-info/std",

"frame-support/std",
"frame/std",

"pallet-balances/std",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ pub mod pallet_foo {
pub some_enum: FooEnum,
pub some_struct: FooStruct,
#[serde(skip)]
_phantom: PhantomData<T>,
pub _phantom: PhantomData<T>,
}

#[pallet::genesis_build]
Expand Down
42 changes: 20 additions & 22 deletions docs/sdk/src/reference_docs/chain_spec_runtime/src/presets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@

//! Presets for the chain-spec demo runtime.

use crate::pallets::{FooEnum, SomeFooData1, SomeFooData2};
use crate::{
pallets::{FooEnum, SomeFooData1, SomeFooData2},
runtime::{BarConfig, FooConfig, RuntimeGenesisConfig},
};
use alloc::vec;
use frame_support::build_struct_json_patch;
use serde_json::{json, to_string, Value};
use sp_application_crypto::Ss58Codec;
use sp_keyring::AccountKeyring;
Expand All @@ -27,7 +31,7 @@ use sp_keyring::AccountKeyring;
pub const PRESET_1: &str = "preset_1";
/// A demo preset with real types.
pub const PRESET_2: &str = "preset_2";
/// Another demo preset with real types.
/// Another demo preset with real types and manually created json object.
pub const PRESET_3: &str = "preset_3";
/// A single value patch preset.
pub const PRESET_4: &str = "preset_4";
Expand Down Expand Up @@ -58,21 +62,21 @@ fn preset_1() -> Value {
}

#[docify::export]
/// Function provides a preset demonstrating how use the actual types to create a preset.
/// Function provides a preset demonstrating how to create a preset using
/// [`build_struct_json_patch`] macro.
fn preset_2() -> Value {
json!({
"bar": {
"initialAccount": AccountKeyring::Ferdie.public().to_ss58check(),
},
"foo": {
"someEnum": FooEnum::Data2(SomeFooData2 { values: vec![12,16] }),
"someInteger": 200
build_struct_json_patch!(RuntimeGenesisConfig {
foo: FooConfig {
some_integer: 200,
some_enum: FooEnum::Data2(SomeFooData2 { values: vec![0x0c, 0x10] })
},
bar: BarConfig { initial_account: Some(AccountKeyring::Ferdie.public().into()) },
})
}

#[docify::export]
/// Function provides a preset demonstrating how use the actual types to create a preset.
/// Function provides a preset demonstrating how use the actual types to manually create a JSON
/// representing the preset.
fn preset_3() -> Value {
json!({
"bar": {
Expand All @@ -92,22 +96,16 @@ fn preset_3() -> Value {

#[docify::export]
/// Function provides a minimal preset demonstrating how to patch single key in
/// `RuntimeGenesisConfig`.
fn preset_4() -> Value {
json!({
"foo": {
"someEnum": {
"Data2": {
"values": "0x0c0f"
}
},
},
/// `RuntimeGenesisConfig` using [`build_struct_json_patch`] macro.
pub fn preset_4() -> Value {
build_struct_json_patch!(RuntimeGenesisConfig {
foo: FooConfig { some_enum: FooEnum::Data2(SomeFooData2 { values: vec![0x0c, 0x10] }) },
})
}

#[docify::export]
/// Function provides an invalid preset demonstrating how important is use of
/// [`deny_unknown_fields`] in data structures used in `GenesisConfig`.
/// `deny_unknown_fields` in data structures used in `GenesisConfig`.
fn preset_invalid() -> Value {
json!({
"foo": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ fn get_preset() {

let output: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();

//note: copy of chain_spec_guide_runtime::preset_1
//note: copy of chain_spec_guide_runtime::preset_2
let expected_output = json!({
"bar": {
"initialAccount": "5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL",
Expand Down Expand Up @@ -186,3 +186,20 @@ fn generate_para_chain_spec() {
});
assert_eq!(output, expected_output, "Output did not match expected");
}

#[test]
#[docify::export]
fn preset_4_json() {
assert_eq!(
chain_spec_guide_runtime::presets::preset_4(),
json!({
"foo": {
"someEnum": {
"Data2": {
"values": "0x0c10"
}
},
},
})
);
}
17 changes: 5 additions & 12 deletions polkadot/runtime/westend/src/genesis_config_presets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use crate::{
#[cfg(not(feature = "std"))]
use alloc::format;
use alloc::{vec, vec::Vec};
use frame_support::build_struct_json_patch;
use pallet_staking::{Forcing, StakerStatus};
use polkadot_primitives::{AccountId, AssignmentId, SchedulerParams, ValidatorId};
use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId;
Expand Down Expand Up @@ -170,7 +171,7 @@ fn westend_testnet_genesis(
const ENDOWMENT: u128 = 1_000_000 * WND;
const STASH: u128 = 100 * WND;

let config = RuntimeGenesisConfig {
build_struct_json_patch!(RuntimeGenesisConfig {
balances: BalancesConfig {
balances: endowed_accounts.iter().map(|k| (k.clone(), ENDOWMENT)).collect::<Vec<_>>(),
},
Expand All @@ -192,7 +193,6 @@ fn westend_testnet_genesis(
)
})
.collect::<Vec<_>>(),
..Default::default()
},
staking: StakingConfig {
minimum_validator_count: 1,
Expand All @@ -204,19 +204,12 @@ fn westend_testnet_genesis(
invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect::<Vec<_>>(),
force_era: Forcing::NotForcing,
slash_reward_fraction: Perbill::from_percent(10),
..Default::default()
},
babe: BabeConfig { epoch_config: BABE_GENESIS_EPOCH_CONFIG, ..Default::default() },
babe: BabeConfig { epoch_config: BABE_GENESIS_EPOCH_CONFIG },
sudo: SudoConfig { key: Some(root_key) },
configuration: ConfigurationConfig { config: default_parachains_host_configuration() },
registrar: RegistrarConfig {
next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID,
..Default::default()
},
..Default::default()
};

serde_json::to_value(config).expect("Could not build genesis config.")
registrar: RegistrarConfig { next_free_para_id: polkadot_primitives::LOWEST_PUBLIC_ID },
})
}

// staging_testnet
Expand Down
18 changes: 18 additions & 0 deletions prdoc/pr_5813.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
title: "build_struct_json_patch 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
- name: westend-runtime
bump: patch
1 change: 1 addition & 0 deletions substrate/frame/support/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ pretty_assertions = { workspace = true }
sp-timestamp = { workspace = true }
frame-system = { workspace = true, default-features = true }
sp-crypto-hashing = { workspace = true, default-features = true }
Inflector = { workspace = true }

[features]
default = ["std"]
Expand Down
Loading

0 comments on commit 7e99621

Please sign in to comment.