//! Ghost chain configuration. use codec::Encode; use authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId; use babe_primitives::AuthorityId as BabeId; use ghost_slow_clap::sr25519::AuthorityId as SlowClapId; use grandpa_primitives::AuthorityId as GrandpaId; #[cfg(feature = "casper-native")] use pallet_staking::Forcing; use primitives::{AccountId, AccountPublic}; #[cfg(feature = "casper-native")] use casper_runtime as casper; #[cfg(feature = "casper-native")] use casper_runtime_constants::currency::{CSPR, STRH}; use sc_chain_spec::ChainSpecExtension; #[cfg(feature = "casper-native")] use sc_chain_spec::ChainType; use serde::{Deserialize, Serialize}; use sp_core::{sr25519, Pair, Public}; use sp_runtime::traits::IdentifyAccount; #[cfg(feature = "casper-native")] use sp_runtime::Perbill; #[cfg(feature = "casper-native")] use telemetry::TelemetryEndpoints; #[cfg(feature = "casper-native")] const CASPER_TELEMETRY_URL: &str = "wss://telemetry.ghostchain.io/submit/"; #[cfg(feature = "casper-native")] const DEFAULT_PROTOCOL_ID: &str = "cspr"; /// Node `ChainSpec` extensions. /// /// Additional parameters for some substrate core modules, /// customizable from the chain spec. #[derive(Default, Clone, Serialize, Deserialize, ChainSpecExtension)] #[serde(rename_all = "camelCase")] pub struct Extensions { /// Block number with known hashes. pub fork_blocks: sc_client_api::ForkBlocks, /// Known bad block hashes. pub bad_blocks: sc_client_api::BadBlocks, /// The light sync state. /// This value will be set by the `sync-state rpc` implementation. pub light_sync_state: sc_sync_state_rpc::LightSyncStateExtension, } // Generic chain spec, in case when we don't have the native runtime. pub type GenericChainSpec = sc_service::GenericChainSpec<(), Extensions>; /// The `ChainSpec` parametrized for the ghost runtime. #[cfg(feature = "casper-native")] pub type CasperChainSpec = sc_service::GenericChainSpec<(), Extensions>; #[cfg(not(feature = "casper-native"))] pub type CasperChainSpec = GenericChainSpec; pub fn casper_config() -> Result { CasperChainSpec::from_json_bytes(&include_bytes!("../chain-specs/casper.json")[..]) } #[cfg(feature = "casper-native")] fn casper_session_keys( babe: BabeId, grandpa: GrandpaId, authority_discovery: AuthorityDiscoveryId, slow_clap: SlowClapId, ) -> casper::opaque::SessionKeys { casper::opaque::SessionKeys { babe, grandpa, authority_discovery, slow_clap, } } pub fn casper_chain_spec_properties() -> serde_json::map::Map { serde_json::json!({ "ss58Format": 1996, "tokenDecimals": 18, "tokenSymbol": "CSPR", }) .as_object() .expect("Map given; qed") .clone() } /// Helper function to generate a crypto pair from seed. pub fn get_from_seed(seed: &str) -> ::Public { TPublic::Pair::from_string(&format!("//{}", seed), None) .expect("static values are valid; qed") .public() } /// Helper function to generate account ID from seed. pub fn get_account_id_from_seed(seed: &str) -> AccountId where AccountPublic: From<::Public>, { AccountPublic::from(get_from_seed::(seed)).into_account() } /// Helper function to generate stash, controller and session key from seed pub fn generate_authority_keys_from_seed( seed: &str, ) -> ( AccountId, AccountId, BabeId, GrandpaId, AuthorityDiscoveryId, SlowClapId, ) { let keys = get_authority_keys_from_seed(seed); (keys.0, keys.1, keys.2, keys.3, keys.4, keys.5) } /// Helper function to generate stash, controller and session key from seed pub fn get_authority_keys_from_seed( seed: &str, ) -> ( AccountId, AccountId, BabeId, GrandpaId, AuthorityDiscoveryId, SlowClapId, ) { ( get_account_id_from_seed::(&format!("{}//stash", seed)), get_account_id_from_seed::(seed), get_from_seed::(seed), get_from_seed::(seed), get_from_seed::(seed), get_from_seed::(seed), ) } #[cfg(feature = "casper-native")] fn casper_testnet_accounts() -> Vec { vec![ get_account_id_from_seed::("Alice"), get_account_id_from_seed::("Bob"), get_account_id_from_seed::("Charlie"), get_account_id_from_seed::("Dave"), get_account_id_from_seed::("Eve"), get_account_id_from_seed::("Feride"), get_account_id_from_seed::("Alice//stash"), get_account_id_from_seed::("Bob//stash"), get_account_id_from_seed::("Charlie//stash"), get_account_id_from_seed::("Dave//stash"), get_account_id_from_seed::("Eve//stash"), get_account_id_from_seed::("Feride//stash"), ] } #[cfg(feature = "casper-native")] fn casper_testnet_evm_accounts() -> Vec<(AccountId, u128, u8)> { vec![ // 01c928771aea942a1e7ac06adf2b73dfbc9a25d9eaa516e3673116af7f345198 ( get_account_id_from_seed::("1A69d2D5568D1878023EeB121a73d33B9116A760"), 1337 * CSPR, 1, ), // b19a435901872f817185f7234a1484eae837613f9d10cf21927a23c2d8cb9139 ( get_account_id_from_seed::("2f86cfBED3fbc1eCf2989B9aE5fc019a837A9C12"), 1337 * CSPR, 2, ), // d3baf57b74d65719b2dc33f5a464176022d0cc5edbca002234229f3e733875fc ( get_account_id_from_seed::("e83f67361Ac74D42A48E2DAfb6706eb047D8218D"), 69 * CSPR, 3, ), // c4683d566436af6b58b4a59c8f501319226e85b21869bf93d5eeb4596d4791d4 ( get_account_id_from_seed::("827ee4ad9b259b6fa1390ed60921508c78befd63"), 69 * CSPR, 4, ), ] } #[cfg(feature = "casper-native")] fn casper_testnet_evm_networks() -> Vec<(u32, Vec)> { vec![( 11155111, ghost_networks::NetworkData { chain_name: "sepolia-ethereum-testnet".into(), default_endpoints: vec![ "https://sepolia.drpc.org".into(), "https://sepolia.gateway.tenderly.co".into(), "https://api.zan.top/eth-sepolia".into(), "https://rpc.sepolia.ethpandaops.io".into(), "https://ethereum-sepolia-rpc.publicnode.com".into(), "https://1rpc.io/sepolia".into(), "https://0xrpc.io/sep".into(), "https://eth-sepolia.api.onfinality.io/public".into(), ], finality_delay: 40u64, rate_limit_delay: 3_000u64, block_distance: 20u64, network_type: ghost_networks::NetworkType::Evm, gatekeeper: "0xc85129A097773B7F8970a7364c928C05f265E6A1".into(), topic_name: "0x7ab52ec05c331e6257a3d705d6bea6e4c27277351764ad139209e06b203811a6".into(), incoming_fee: 69_000_000u32, outgoing_fee: 0u32, } .encode(), )] } /// Helper function to create casper `GenesisConfig` for testing #[cfg(feature = "casper-native")] pub fn testnet_config_genesis( initial_authorities: Vec<( AccountId, AccountId, BabeId, GrandpaId, AuthorityDiscoveryId, SlowClapId, )>, endowed_accounts: Option>, ghost_accounts: Option>, evm_networks: Option)>>, ) -> serde_json::Value { let endowed_accounts: Vec = endowed_accounts.unwrap_or_else(casper_testnet_accounts); let ghost_accounts: Vec<(AccountId, u128, u8)> = ghost_accounts.unwrap_or_default(); let evm_networks: Vec<(u32, Vec)> = evm_networks.unwrap_or_default(); const ENDOWMENT: u128 = 1_000 * CSPR; const STASH: u128 = 500 * CSPR; serde_json::json!({ "balances": { "balances": endowed_accounts .iter() .map(|k| (k.clone(), ENDOWMENT)) .chain(ghost_accounts .iter() .map(|k| (k.0.clone(), k.1.clone()))) .collect::>(), }, "session": { "keys": initial_authorities .iter() .map(|x| { ( x.0.clone(), x.0.clone(), casper_session_keys( x.2.clone(), x.3.clone(), x.4.clone(), x.5.clone(), ), ) }) .collect::>(), }, "staking": { "validatorCount": initial_authorities.len() as u32, "minimumValidatorCount": 1, "invulnerables": initial_authorities .iter() .map(|x| x.0.clone()) .collect::>(), "forceEra": Forcing::NotForcing, "slashRewardFraction": Perbill::from_percent(10), "stakers": initial_authorities .iter() .map(|x| (x.0.clone(), x.0.clone(), STASH, casper::StakerStatus::::Validator)) .collect::>(), }, "babe": { "epochConfig": Some(casper::BABE_GENESIS_EPOCH_CONFIG), }, "ghostSudo": { "key": endowed_accounts .first() .cloned(), }, "ghostNetworks": { "networks": evm_networks, }, "ghostClaims": { "total": ghost_accounts.iter().fold(0, |acc, k| acc + k.1), "membersAndRanks": ghost_accounts .iter() .map(|k| (k.0.clone(), k.2.clone())) .collect::>(), }, }) } #[cfg(feature = "casper-native")] fn casper_staging_config_genesis() -> serde_json::Value { use hex_literal::hex; use sp_core::crypto::UncheckedInto; // Following keys are used in genesis config for testing (casper) chains. // DO NOT use them in production chains such as ghost. let endowed_accounts = vec![ // sfErNwRgZ6ypB7wY8M2smXMZjxqUkc2TgUcNvC1JNQJFXS8bw hex!["328d3b7c3046ef7700937d99fb2e98ce2591682c2b5dcf3f562e4da157650237"].into(), // sfEwRjyvEQcpRQ1qbCZum27nEkTggKEt7DtqxwyYQULt9UuUN hex!["3666e4e19f87bb8680495f31864ce1f1c69d4178002cc01911aef2cc7313f203"].into(), // sfFD7KSRi2aSREJWc9X75sVTN4a5pbPM5VSSJefmPvMuiVbPr hex!["425ccd7bda4f5c76788ba23bc0381d7a2e496179c93301208c57501c80a4232a"].into(), // sfH29zyFYtoFj6fSXskAEXG5XyyMCa5YycahJkYMwSUz8CcEU hex!["927a98dcf8f721103005f168476c24b91d7d10d580f457006a908e10e62c7729"].into(), ]; let initial_authorities: Vec<( AccountId, AccountId, BabeId, GrandpaId, AuthorityDiscoveryId, SlowClapId, )> = vec![ ( // sfFXZmnDVnkQ781J2gbqUpi7K5KgMWMdM4eeii74xxGgKYnNN hex!["507045c82be367f95408466cd054ca39bfa52697a3ef22809af14cf9de304f02"].into(), // sfFXZmnDVnkQ781J2gbqUpi7K5KgMWMdM4eeii74xxGgKYnNN hex!["507045c82be367f95408466cd054ca39bfa52697a3ef22809af14cf9de304f02"].into(), // sfJeojACBa7WiH6tBwikBKAMU2oKmseEBD1GYUYATvfWuLcPa hex!["daaaaab6a6e574099e24ae9bb75b543610edef9d374fa85a378edb573b47615f"] .unchecked_into(), // sfFdtzNxJdeEkgHxvk144rJKxf7wcYvgX5tqfgZRutW9YvAKE hex!["55446f9a7aa99ced06b317c80ce90d56b84e56526775683af2525969e8da0b64"] .unchecked_into(), // sfE8gsMYAjAJHk5gyYZN7AW6pfmJ7V9H7xxWto24nmhzCUXaQ hex!["12c14850562021eb99f58f90ab624fb6cfaf3ac9228a92f8b60115fe6a6af15a"] .unchecked_into(), // sfE3GKSrKZzrZpdapJ2VGRpPor45T4D4i8QBZNumSNGqGv7PX hex!["0e9e698c7b2bf5ce3861cb4bc4ddf9e200237c282025b093ada850d764d12a35"] .unchecked_into(), ), ( // sfHLqWNC4hMKHhwvPWmWcxZsDPhCTQKgh1Ap7pm3qML5GBTBa hex!["a0ba0196e6ee7e6b5b0553035c5cb5c04e9725001b5732839d0529cbc00c9600"].into(), // sfHLqWNC4hMKHhwvPWmWcxZsDPhCTQKgh1Ap7pm3qML5GBTBa hex!["a0ba0196e6ee7e6b5b0553035c5cb5c04e9725001b5732839d0529cbc00c9600"].into(), // sfGA6tPPF8dAc8QpMCMjxitG3j8sXPhkdpm5bwz4UsXAApUiw hex!["6c4dd88b43e2011cf9a6a73d53446336ac9e04cdd4ca23587df63187ac455e49"] .unchecked_into(), // sfGxQZXFUQH1AXv82rpjiJHFs7YsdEuVGdyvKiS2Tajpvw6Se hex!["8f9ea20bf4a807a8e710f7559dece86e94672b5b361de157bdaa5c1f37849f8d"] .unchecked_into(), // sfGz2enFUR22cQ5ey61MdtPqbCeEWZA1wsCFFSLGaK7vKnv8C hex!["90db5ed339a559ed157995a48d781f44c7df972dfba4bc855e4b59fa46438e17"] .unchecked_into(), // sfEtwe5BoroNjkdLsvnjnMemUKiw8MS1X4YW8bepbbGvhS4LZ hex!["3481cdcbcf37a4669c29a78cf9ceb39383a10ef0a18b36b92d149fdd0c24ae00"] .unchecked_into(), ), ( // sfG9iWUS7AKBzvqdz3uDZv7f1t79vvSTYL1VxzYPHjhmLnQVn hex!["6c0283f4c688f0e75ad546c790bbd5961c1a6931543aa589f368f8272c44b758"].into(), // sfG9iWUS7AKBzvqdz3uDZv7f1t79vvSTYL1VxzYPHjhmLnQVn hex!["6c0283f4c688f0e75ad546c790bbd5961c1a6931543aa589f368f8272c44b758"].into(), // sfGMUYXSjHgwGBpbGiHFoqT1DdJwZdHk49H5ViaQM7HUnayvZ hex!["74fa7381a7a74b316afb6793a00387eed9d95d46a69866cbb316b5d9c918af0e"] .unchecked_into(), // sfEWYhczV6PbmeNXZTcA4LvhpzvCfNMatYoSNKvDphmNaQzqB hex!["236d2fa03f4ed8cb65de7e514d7540159b328f1c170dd402b094ad7fbf547218"] .unchecked_into(), // sfDirg32ityDvrjxh4822unhP4qm4S9yTqN99gesN322swn7h hex!["00946618c353e4c6546b87f9ca1089b846b0ea4658ee8e6d9d1200c24cb5ee27"] .unchecked_into(), // sfFZgp1Z5diFAZ16swuQD5GojGCsMMFVR19uWnCrppMLuYjRv hex!["520e74f8c5853ec8577932327ad3247656db25b74c79ad09adb431b271002401"] .unchecked_into(), ), ( // sfHjtrXFzRmxwjE4rjVxFJXpvVv7furjdymZS7PQRFiANpodz hex!["b24feb55b2cac4b365a9245c2a97525b01bd1a594d2d42b91f6bc38c9c2e6517"].into(), // sfHjtrXFzRmxwjE4rjVxFJXpvVv7furjdymZS7PQRFiANpodz hex!["b24feb55b2cac4b365a9245c2a97525b01bd1a594d2d42b91f6bc38c9c2e6517"].into(), // sfF5XWwvNPjZEsBz1HWs5Ys5zcE85UHnN1BV8TBBectqFQZRm hex!["3c944c704cae203619b9e7a5a4b6742736da6a8e76c762291bebdc7652cfec2f"] .unchecked_into(), // sfDx3gj4wFHg3cK6bopnypHQ6TxW1VgyPsYVbXKhRHtLDLxQb hex!["0aa3a88f6b777c95c3dfe7e997b76798413f16aa325f34824cae0c9102b281d5"] .unchecked_into(), // sfHXZbnZV3YWwnH28q2xumm7stvuC8LweYYxPNuHGonnX7QHL hex!["a8e828d10cf7b74481b6e746e5532d4740ea8014a0d3d856540a59847f8a6b76"] .unchecked_into(), // sfEaHBrBgeMhRTA3WHE9Nbyvn3h7xkhYKi7go4yo81L88o9zJ hex!["2645f1f6820dd3a917eebbdab033088d8862477c1c14759b218685f9a0893377"] .unchecked_into(), ), ]; let ghost_accounts: Vec<(AccountId, u128, u8)> = casper_testnet_evm_accounts(); let evm_networks = casper_testnet_evm_networks(); const ENDOWMENT: u128 = 31 * CSPR; const STASH: u128 = 69 * CSPR; serde_json::json!({ "balances": { "balances": endowed_accounts .iter() .map(|k: &AccountId| (k.clone(), ENDOWMENT)) .chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH))) .collect::>(), }, "session": { "keys": initial_authorities .iter() .map(|x| { ( x.0.clone(), x.0.clone(), casper_session_keys( x.2.clone(), x.3.clone(), x.4.clone(), x.5.clone(), ), ) }) .collect::>(), }, "staking": { "validatorCount": 500, "minimumValidatorCount": 3, "stakers": initial_authorities .iter() .map(|x| { ( x.0.clone(), x.0.clone(), STASH, casper::StakerStatus::::Validator, ) }) .collect::>(), "forceEra": Forcing::NotForcing, "slashRewardFraction": Perbill::from_percent(10), "minNominatorBond": 6_900 * STRH, "minValidatorBond": 6_900 * STRH, }, "babe": { "epochConfig": Some(casper::BABE_GENESIS_EPOCH_CONFIG), }, "ghostSudo": { "key": endowed_accounts .first() .cloned(), }, "ghostNetworks": { "networks": evm_networks, }, "ghostClaims": { "total": ghost_accounts .iter() .fold(0, |acc, k| acc + k.1), "membersAndRanks": ghost_accounts .iter() .map(|k| (k.0.clone(), k.2.clone())) .collect::>(), }, }) } #[cfg(feature = "casper-native")] fn casper_development_config_genesis() -> serde_json::Value { testnet_config_genesis( vec![get_authority_keys_from_seed("Alice")], None, None, None, ) } #[cfg(feature = "casper-native")] fn casper_local_config_genesis() -> serde_json::Value { testnet_config_genesis( vec![ get_authority_keys_from_seed("Alice"), get_authority_keys_from_seed("Bob"), ], Some(casper_testnet_accounts()), Some(casper_testnet_evm_accounts()), Some(casper_testnet_evm_networks()), ) } #[cfg(feature = "casper-native")] pub fn casper_development_config() -> Result { Ok(CasperChainSpec::builder( casper::WASM_BINARY.ok_or("Casper development wasm not available")?, Default::default(), ) .with_name("Development") .with_id("casper_dev") .with_chain_type(ChainType::Development) .with_genesis_config_patch(casper_development_config_genesis()) .with_protocol_id(DEFAULT_PROTOCOL_ID) .with_properties(casper_chain_spec_properties()) .build()) } #[cfg(feature = "casper-native")] pub fn casper_local_testnet_config() -> Result { Ok(CasperChainSpec::builder( casper::WASM_BINARY.ok_or("Casper local testnet wasm not available")?, Default::default(), ) .with_name("Casper Local Testnet") .with_id("casper_local_testnet") .with_chain_type(ChainType::Local) .with_genesis_config_patch(casper_local_config_genesis()) .with_protocol_id(DEFAULT_PROTOCOL_ID) .with_properties(casper_chain_spec_properties()) .build()) } #[cfg(feature = "casper-native")] pub fn casper_staging_testnet_config() -> Result { Ok(CasperChainSpec::builder( casper::WASM_BINARY.ok_or("Casper staging testnet wasm not available")?, Default::default(), ) .with_name("Casper Staging Testnet") .with_id("casper_staging_testnet") .with_chain_type(ChainType::Live) .with_genesis_config_patch(casper_staging_config_genesis()) .with_telemetry_endpoints( TelemetryEndpoints::new(vec![(CASPER_TELEMETRY_URL.to_string(), 0)]) .expect("Casper Staging telemetry url is valid; qed"), ) .with_protocol_id(DEFAULT_PROTOCOL_ID) .with_properties(casper_chain_spec_properties()) .build()) }