1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// KILT Blockchain – https://botlabs.org
// Copyright (C) 2019-2024 BOTLabs GmbH

// The KILT Blockchain is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// The KILT Blockchain is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

// If you feel like getting in touch with us, you can do so at info@botlabs.org

//! KILT chain specification

use kestrel_runtime::{
	opaque::SessionKeys, BalancesConfig, RuntimeGenesisConfig, SessionConfig, SudoConfig, SystemConfig, WASM_BINARY,
};
use runtime_common::{AccountId, AccountPublic};

use sc_service::{self, ChainType, Properties};
use serde_json::to_value;
use sp_consensus_aura::ed25519::AuthorityId as AuraId;
use sp_consensus_grandpa::AuthorityId as GrandpaId;
use sp_core::{ed25519, sr25519, Pair, Public};
use sp_runtime::traits::IdentifyAccount;

pub(crate) fn load_spec(id: &str) -> Result<Box<dyn sc_service::ChainSpec>, String> {
	let chain_spec = match id {
		// Dev chainspec, used for SDK integration tests
		"dev" => Ok::<_, String>(generate_dev_chain_spec()),
		_ => return Err(format!("Unknown spec: {}", id)),
	}?;
	Ok(Box::new(chain_spec))
}

type ChainSpec = sc_service::GenericChainSpec<RuntimeGenesisConfig>;

fn generate_dev_chain_spec() -> ChainSpec {
	let wasm_binary = WASM_BINARY.expect("Development WASM binary not available");
	let properties = Properties::from_iter([
		("tokenDecimals".into(), 15.into()),
		("tokenSymbol".into(), "DILT".into()),
	]);
	let genesis_state = to_value(generate_genesis_state()).expect("Creating genesis state failed");

	ChainSpec::builder(wasm_binary, None)
		.with_name("Standalone Node (Dev)")
		.with_id("standalone_node_development")
		.with_chain_type(ChainType::Development)
		.with_properties(properties)
		.with_genesis_config(genesis_state)
		.build()
}

fn generate_genesis_state() -> RuntimeGenesisConfig {
	let endowed_accounts = vec![
		// Dev Faucet account
		get_account_id_from_secret::<ed25519::Public>(
			"receive clutch item involve chaos clutch furnace arrest claw isolate okay together",
		),
		get_account_id_from_secret::<ed25519::Public>("//Alice"),
		get_account_id_from_secret::<ed25519::Public>("//Bob"),
		get_account_id_from_secret::<sr25519::Public>("//Alice"),
		get_account_id_from_secret::<sr25519::Public>("//Bob"),
	];
	let initial_authorities = vec![get_authority_keys_from_secret("//Alice")];
	let root_key = get_account_id_from_secret::<ed25519::Public>("//Alice");

	RuntimeGenesisConfig {
		system: SystemConfig { ..Default::default() },
		balances: BalancesConfig {
			balances: endowed_accounts.into_iter().map(|a| (a, 1u128 << 90)).collect(),
		},
		session: SessionConfig {
			keys: initial_authorities
				.into_iter()
				.map(|(acc, aura, grandpa)| {
					(
						acc.clone(),                          // account id
						acc,                                  // validator id
						template_session_keys(aura, grandpa), // session keys
					)
				})
				.collect::<Vec<_>>(),
		},
		sudo: SudoConfig { key: Some(root_key) },
		..Default::default()
	}
}

fn template_session_keys(aura_keys: AuraId, grandpa_keys: GrandpaId) -> SessionKeys {
	SessionKeys {
		aura: aura_keys,
		grandpa: grandpa_keys,
	}
}

fn get_authority_keys_from_secret(seed: &str) -> (AccountId, AuraId, GrandpaId) {
	(
		get_account_id_from_secret::<ed25519::Public>(seed),
		get_public_key_from_secret::<AuraId>(seed),
		get_public_key_from_secret::<GrandpaId>(seed),
	)
}

fn get_account_id_from_secret<TPublic: Public>(seed: &str) -> AccountId
where
	AccountPublic: From<<TPublic::Pair as Pair>::Public>,
{
	AccountPublic::from(get_public_key_from_secret::<TPublic>(seed)).into_account()
}

fn get_public_key_from_secret<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
	TPublic::Pair::from_string(seed, None)
		.unwrap_or_else(|_| panic!("Invalid string '{}'", seed))
		.public()
}