diff --git a/prdoc/pr_7368.prdoc b/prdoc/pr_7368.prdoc new file mode 100644 index 0000000000000000000000000000000000000000..42e32c9d50354acbf4ec376d82750bdcbc121b67 --- /dev/null +++ b/prdoc/pr_7368.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: "Add chain properties to chain-spec-builder" + +doc: + - audience: Node Dev + description: | + - Adds support for chain properties to chain-spec-builder. + +crates: + - name: staging-chain-spec-builder + bump: minor diff --git a/substrate/bin/utils/chain-spec-builder/src/lib.rs b/substrate/bin/utils/chain-spec-builder/src/lib.rs index 73c2868b3312e9c1f66540830000d14b36e4bdb1..972958eda439e43434a9373f24ba1f0c76c28bef 100644 --- a/substrate/bin/utils/chain-spec-builder/src/lib.rs +++ b/substrate/bin/utils/chain-spec-builder/src/lib.rs @@ -83,6 +83,18 @@ pub struct CreateCmd { /// errors will be reported. #[arg(long, short = 'v')] verify: bool, + /// Chain properties in `KEY=VALUE` format. + /// + /// Multiple `KEY=VALUE` entries can be specified and separated by a comma. + /// + /// Example: `--properties tokenSymbol=UNIT,tokenDecimals=12,ss58Format=42,isEthereum=false` + /// Or: `--properties tokenSymbol=UNIT --properties tokenDecimals=12 --properties ss58Format=42 + /// --properties=isEthereum=false` + /// + /// The first uses comma as separation and the second passes the argument multiple times. Both + /// styles can also be mixed. + #[arg(long, default_value = "tokenSymbol=UNIT,tokenDecimals=12")] + pub properties: Vec<String>, #[command(subcommand)] action: GenesisBuildAction, @@ -385,15 +397,44 @@ impl CreateCmd { } } -/// Processes `CreateCmd` and returns string represenataion of JSON version of `ChainSpec`. +/// Parses chain properties passed as a comma-separated KEY=VALUE pairs. +fn parse_properties(raw: &String, props: &mut sc_chain_spec::Properties) -> Result<(), String> { + for pair in raw.split(',') { + let mut iter = pair.splitn(2, '='); + let key = iter + .next() + .ok_or_else(|| format!("Invalid chain property key: {pair}"))? + .trim() + .to_owned(); + let value_str = iter + .next() + .ok_or_else(|| format!("Invalid chain property value for key: {key}"))? + .trim(); + + // Try to parse as bool, number, or fallback to String + let value = match value_str.parse::<bool>() { + Ok(b) => Value::Bool(b), + Err(_) => match value_str.parse::<u32>() { + Ok(i) => Value::Number(i.into()), + Err(_) => Value::String(value_str.to_string()), + }, + }; + + props.insert(key, value); + } + Ok(()) +} + +/// Processes `CreateCmd` and returns string representation of JSON version of `ChainSpec`. pub fn generate_chain_spec_for_runtime(cmd: &CreateCmd) -> Result<String, String> { let code = cmd.get_runtime_code()?; let chain_type = &cmd.chain_type; let mut properties = sc_chain_spec::Properties::new(); - properties.insert("tokenSymbol".into(), "UNIT".into()); - properties.insert("tokenDecimals".into(), 12.into()); + for raw in &cmd.properties { + parse_properties(raw, &mut properties)?; + } let builder = ChainSpec::builder(&code[..], Default::default()) .with_name(&cmd.chain_name[..]) diff --git a/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_properties.json b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_properties.json new file mode 100644 index 0000000000000000000000000000000000000000..5bf7ab9bed1262411a48c6a4ed7317622087476d --- /dev/null +++ b/substrate/bin/utils/chain-spec-builder/tests/expected/create_with_properties.json @@ -0,0 +1,40 @@ +{ + "name": "Custom", + "id": "custom", + "chainType": "Live", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": null, + "properties": { + "tokenDecimals": 6, + "tokenSymbol": "TEST", + "ss58Prefix": 42, + "isEthereum": false + }, + "codeSubstitutes": {}, + "genesis": { + "runtimeGenesis": { + "code": "0x010203", + "config": { + "babe": { + "authorities": [], + "epochConfig": { + "allowed_slots": "PrimaryAndSecondaryVRFSlots", + "c": [ + 1, + 4 + ] + } + }, + "balances": { + "balances": [], + "devAccounts": null + }, + "substrateTest": { + "authorities": [] + }, + "system": {} + } + } + } +} diff --git a/substrate/bin/utils/chain-spec-builder/tests/test.rs b/substrate/bin/utils/chain-spec-builder/tests/test.rs index 5ac687d75fd4454e1e2452e0b001bc435279efd9..10ea45247cdca1b1cb0688377b5ee5e73d559cfb 100644 --- a/substrate/bin/utils/chain-spec-builder/tests/test.rs +++ b/substrate/bin/utils/chain-spec-builder/tests/test.rs @@ -234,6 +234,29 @@ fn test_add_code_substitute() { assert_output_eq_expected(true, SUFFIX, "tests/expected/add_code_substitute.json"); } +#[test] +fn test_create_with_properties() { + const SUFFIX: &str = "11"; + let mut builder = get_builder( + SUFFIX, + vec![ + "create", + "-r", + DUMMY_PATH, + "--properties", + "tokenSymbol=TEST,tokenDecimals=6", + "--properties", + "isEthereum=false", + "--properties", + "ss58Prefix=42", + "default", + ], + ); + builder.set_create_cmd_runtime_code(substrate_test_runtime::WASM_BINARY.unwrap().into()); + builder.run().unwrap(); + assert_output_eq_expected(true, SUFFIX, "tests/expected/create_with_properties.json"); +} + #[docify::export_content] fn cmd_create_default(runtime_path: &str) -> String { bash!(