Files
arkhe-origin-chain/crates/origin-chain-node/src/chain_spec.rs
T
davidherranandClaude Opus 4.7 a2925f67b9 feat(node): Phase 1.6+ Slice 3 — ChainSpec builder canonical mock-WASM fallback v0.0.13
Real ChainSpec JSON builder canonical via mock-WASM fallback pattern. Avanza
Origin Chain sin esperar Slice 2c WASM_BINARY resolution canonical (Larihana
pending). Own minimal JSON builder canonical via serde_json puro vs sc-chain-spec
dep canonical (43/44.0.0 trae sc-network mismatch + cid 0.9.0 yanked core2).

Deliverables canonical:
- crates/origin-chain-node/src/chain_spec.rs NEW (240 LOC) — 3 builder fns
  (development/local_testnet/mainnet) + load_spec alias resolver + serialize_chain_spec
- crates/origin-chain-node/src/main.rs — new BuildSpec subcommand + build_spec fn
- crates/origin-chain-node/Cargo.toml — Slice 3 dep block canonical doc
- Cargo.toml workspace — sc-chain-spec investigation findings registered

Output JSON canonical compatible con Substrate-canonical sc_chain_spec::GenericChainSpec
schema (12-field structure). Mock WASM hex = "0x" placeholder canonical mientras
Slice 2c land canonical real WASM_BINARY via substrate-wasm-builder.

Verification canonical end-to-end:
- cargo check workspace verde 0.60s
- 10/10 chain_spec tests canonical pass + 75+ workspace tests canonical no regression
- cargo build --release origin-chain-node 2.28s clean
- build-spec --chain dev|local|mainnet|--raw all functional canonical
- BM V2 mainnet allocation verified: 400M+250M+150M+150M+50M = 1B OGC canonical
- Existing subcommands (version, list-presets, print-preset, info) NO REGRESSION

Phase 1.6+ progress: Slices 1+2+2b+3 ✅, Slice 2c WASM pending Larihana,
Slice 4 full sc-service requires Slice 2c canonical resolution.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 07:54:57 -05:00

288 lines
11 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! # 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<Value, String> {
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<Value, String> {
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::<String, Value>::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<Value, String> {
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<Value, String> {
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<Value, String> {
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<Value, String> {
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<String, String> {
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());
}
}