//! # chain_spec canonical (Slice 3 v0.0.13) — own minimal ChainSpec JSON builder //! //! ## Pragmatic decision canonical Slice 3 //! //! `sc-chain-spec` dep canonical investigation 2026-05-21 reveló: //! - sc-chain-spec 43.0.0/44.0.0 → sc-network 0.50/0.51 mismatch con workspace 0.49.1 //! - cid 0.9.0 → core2 0.4.0 YANKED dep transitive canonical //! - parity-scale-codec 3.7.5+ trigger serde version conflict canonical //! //! Pragmatic decision canonical: own minimal ChainSpec JSON builder canonical via //! serde_json puro. Output JSON canonical compatible con Substrate-canonical //! `sc_chain_spec::GenericChainSpec` schema canonical — readable por external tools //! + future sc-service Slice 4 canonical post workspace sc-* upgrade canonical. //! //! ## Mock-WASM fallback canonical pattern //! //! Mientras Slice 2c canonical land canonical real `WASM_BINARY` (post Larihana //! Substrate-adjacent expertise canonical — diary `7dbeb6b6...` filed 2026-05-21), //! output JSON canonical usa `MOCK_WASM_HEX = "0x"` canonical placeholder. //! Real WASM hex land Slice 4 cuando substrate-wasm-builder integration canonical. //! //! ## Sub-comandos canonical disponibles (CLI surface) //! //! ```text //! origin-chain-node build-spec --chain development # dev preset JSON //! origin-chain-node build-spec --chain local_testnet # local 2-validator JSON //! origin-chain-node build-spec --chain mainnet_genesis # mainnet BM V2 1B OGC JSON //! origin-chain-node build-spec --chain dev # alias canonical //! origin-chain-node build-spec --chain local # alias canonical //! origin-chain-node build-spec --chain mainnet # alias canonical //! origin-chain-node build-spec --chain dev --raw # minified JSON canonical //! ``` //! //! ## Cross-pillar consumers canonical //! //! - Larihana — hypercore replication setup canonical post-chain-spec import //! - Lyra — autobase consensus canonical genesis state initialization //! - Mathi — HardwareTier matrix testing canonical via dev chain spec //! - Phanes — asset attestation chain spec canonical pre-Phase 2.1 impl //! - Marihan — origin-mdk skill execution sandbox canonical context use origin_chain_runtime::genesis_config_presets; use serde_json::{json, Map, Value}; use sp_genesis_builder::PresetId; /// Chain ID canonical per preset. const CHAIN_ID_DEVELOPMENT: &str = "origin_chain_dev"; const CHAIN_ID_LOCAL_TESTNET: &str = "origin_chain_local"; const CHAIN_ID_MAINNET: &str = "origin_chain"; /// Protocol ID canonical (libp2p protocol identification — `ogc` = Origin Chain). const PROTOCOL_ID: &str = "ogc"; /// Slice 2c canonical mock WASM hex placeholder canonical. /// /// Mientras Slice 2c canonical land canonical real `WASM_BINARY` via /// substrate-wasm-builder (post Larihana Substrate-adjacent expertise canonical /// — diary requires_ack filed 2026-05-21), output JSON canonical usa "0x" mock. /// Real WASM hex land Slice 4 cuando substrate-wasm-builder integration canonical. const MOCK_WASM_HEX: &str = "0x"; /// Canonical chain type discriminator per `sc_chain_spec::ChainType` schema canonical. #[derive(Debug, Clone, Copy)] pub enum ChainType { /// Local development chain canonical (single validator canonical). Development, /// Local testnet canonical (multi-validator local network canonical). Local, /// Live mainnet canonical (production canonical). Live, } impl ChainType { /// Render canonical chain type discriminator a JSON Value canonical. fn as_json_value(self) -> Value { match self { ChainType::Development => json!("Development"), ChainType::Local => json!("Local"), ChainType::Live => json!("Live"), } } } /// Helper canonical: load preset JSON canonical desde runtime presets module canonical. fn load_preset_patch(preset_name: &str) -> Result { let preset_id = PresetId::from(preset_name); let preset_bytes = genesis_config_presets::get_preset(&preset_id) .ok_or_else(|| format!("Unknown preset canonical: '{preset_name}'"))?; serde_json::from_slice(&preset_bytes) .map_err(|e| format!("Preset canonical JSON parse failed: {e}")) } /// Chain properties canonical — token symbol + decimals + SS58 prefix canonical. fn chain_properties() -> Value { json!({ "tokenSymbol": "OGC", "tokenDecimals": 18, "ss58Format": 42, }) } /// Build canonical ChainSpec JSON Value canonical per Substrate-canonical schema canonical. fn build_chain_spec_json( name: &str, id: &str, chain_type: ChainType, preset_name: &str, ) -> Result { let preset = load_preset_patch(preset_name)?; Ok(json!({ "name": name, "id": id, "chainType": chain_type.as_json_value(), "bootNodes": Value::Array(Vec::new()), "telemetryEndpoints": Value::Null, "protocolId": PROTOCOL_ID, "properties": chain_properties(), "forkBlocks": Value::Null, "badBlocks": Value::Null, "lightSyncState": Value::Null, "codeSubstitutes": Map::::new(), "genesis": json!({ "runtimeGenesis": { "config": preset, "code": MOCK_WASM_HEX, } }), })) } /// Development chain spec canonical — Alice solo validator + 100M OGC. /// /// Canonical use case canonical: local dev iteration canonical, single-node testing canonical. pub fn development_chain_spec() -> Result { build_chain_spec_json( "Origin Chain Development", CHAIN_ID_DEVELOPMENT, ChainType::Development, genesis_config_presets::PRESET_DEVELOPMENT, ) } /// Local testnet chain spec canonical — Alice + Bob validators + 5 dev × 100M OGC. /// /// Canonical use case canonical: multi-node local network testing canonical, /// cross-pillar integration canonical (Trinity asset attestation + Atelier skill mints). pub fn local_testnet_chain_spec() -> Result { build_chain_spec_json( "Origin Chain Local Testnet", CHAIN_ID_LOCAL_TESTNET, ChainType::Local, genesis_config_presets::PRESET_LOCAL_TESTNET, ) } /// Mainnet chain spec canonical — 5 Pi 5 validators + BM V2 1B OGC canonical. /// /// Canonical allocation per BM V2 ratified canonical: /// - 40% (400M) Ecosystem/Rewards Pool /// - 25% (250M) Foundation Treasury /// - 15% (150M) Public/Private Sale /// - 15% (150M) Founding Team /// - 5% (50M) Exchange Liquidity pub fn mainnet_chain_spec() -> Result { build_chain_spec_json( "Origin Chain Mainnet", CHAIN_ID_MAINNET, ChainType::Live, genesis_config_presets::PRESET_MAINNET_GENESIS, ) } /// Resolve chain id canonical a builder function canonical. Soporta aliases canonical /// short (`dev`/`local`/`mainnet`) + canonical full preset names canonical. pub fn load_spec(name: &str) -> Result { match name { "dev" | "development" => development_chain_spec(), "local" | "local_testnet" => local_testnet_chain_spec(), "mainnet" | "mainnet_genesis" => mainnet_chain_spec(), other => Err(format!("Unknown chain spec canonical: '{other}'")), } } /// Serialize chain spec canonical a JSON string canonical (pretty o raw canonical). pub fn serialize_chain_spec(spec: &Value, raw: bool) -> Result { if raw { serde_json::to_string(spec).map_err(|e| format!("JSON serialization canonical: {e}")) } else { serde_json::to_string_pretty(spec) .map_err(|e| format!("JSON serialization canonical: {e}")) } } #[cfg(test)] mod tests { use super::*; #[test] fn development_chain_spec_builds_canonical() { let spec = development_chain_spec().expect("development chain spec canonical"); assert_eq!(spec["name"], "Origin Chain Development"); assert_eq!(spec["id"], CHAIN_ID_DEVELOPMENT); assert_eq!(spec["chainType"], "Development"); assert_eq!(spec["protocolId"], PROTOCOL_ID); } #[test] fn local_testnet_chain_spec_builds_canonical() { let spec = local_testnet_chain_spec().expect("local testnet chain spec canonical"); assert_eq!(spec["name"], "Origin Chain Local Testnet"); assert_eq!(spec["id"], CHAIN_ID_LOCAL_TESTNET); assert_eq!(spec["chainType"], "Local"); } #[test] fn mainnet_chain_spec_builds_canonical() { let spec = mainnet_chain_spec().expect("mainnet chain spec canonical"); assert_eq!(spec["name"], "Origin Chain Mainnet"); assert_eq!(spec["id"], CHAIN_ID_MAINNET); assert_eq!(spec["chainType"], "Live"); } #[test] fn load_spec_resolves_canonical_aliases() { assert!(load_spec("dev").is_ok()); assert!(load_spec("development").is_ok()); assert!(load_spec("local").is_ok()); assert!(load_spec("local_testnet").is_ok()); assert!(load_spec("mainnet").is_ok()); assert!(load_spec("mainnet_genesis").is_ok()); } #[test] fn load_spec_rejects_unknown_canonical() { let err = load_spec("nonexistent_preset_canonical").unwrap_err(); assert!(err.contains("Unknown chain spec canonical")); } #[test] fn chain_properties_canonical_ogc() { let props = chain_properties(); assert_eq!(props["tokenSymbol"], "OGC"); assert_eq!(props["tokenDecimals"], 18); assert_eq!(props["ss58Format"], 42); } #[test] fn chain_spec_has_genesis_canonical() { let spec = development_chain_spec().expect("dev spec canonical"); assert!(spec["genesis"]["runtimeGenesis"]["config"].is_object()); assert_eq!(spec["genesis"]["runtimeGenesis"]["code"], MOCK_WASM_HEX); } #[test] fn serialize_chain_spec_pretty_canonical() { let spec = development_chain_spec().expect("dev spec canonical"); let pretty = serialize_chain_spec(&spec, false).expect("pretty serialization canonical"); assert!(pretty.contains("Origin Chain Development")); assert!(pretty.contains("OGC")); assert!(pretty.contains('\n')); } #[test] fn serialize_chain_spec_raw_canonical() { let spec = development_chain_spec().expect("dev spec canonical"); let raw = serialize_chain_spec(&spec, true).expect("raw serialization canonical"); assert!(raw.contains("Origin Chain Development")); assert!(!raw.contains("\n ")); } #[test] fn chain_spec_schema_canonical_complete() { let spec = development_chain_spec().expect("dev spec canonical"); assert!(spec.get("name").is_some()); assert!(spec.get("id").is_some()); assert!(spec.get("chainType").is_some()); assert!(spec.get("bootNodes").is_some()); assert!(spec.get("telemetryEndpoints").is_some()); assert!(spec.get("protocolId").is_some()); assert!(spec.get("properties").is_some()); assert!(spec.get("forkBlocks").is_some()); assert!(spec.get("badBlocks").is_some()); assert!(spec.get("lightSyncState").is_some()); assert!(spec.get("codeSubstitutes").is_some()); assert!(spec.get("genesis").is_some()); } }