1
// Copyright 2019-2025 PureStake Inc.
2
// This file is part of Moonbeam.
3

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

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

            
14
// You should have received a copy of the GNU General Public License
15
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! Moonbeam Chain Specifications and utilities for building them.
18
//!
19
//! Learn more about Substrate chain specifications at
20
//! https://substrate.dev/docs/en/knowledgebase/integrate/chain-spec
21

            
22
#[cfg(test)]
23
use crate::chain_spec::{derive_bip44_pairs_from_mnemonic, get_account_id_from_pair};
24
use crate::chain_spec::{generate_accounts, get_from_seed, Extensions};
25
use crate::HostFunctions;
26
use cumulus_primitives_core::ParaId;
27
use hex_literal::hex;
28
use moonbeam_runtime::{
29
	currency::{GLMR, SUPPLY_FACTOR},
30
	genesis_config_preset::testnet_genesis,
31
	AccountId, WASM_BINARY,
32
};
33
use nimbus_primitives::NimbusId;
34
use sc_service::ChainType;
35
#[cfg(test)]
36
use sp_core::ecdsa;
37

            
38
/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type.
39
pub type ChainSpec = sc_service::GenericChainSpec<Extensions, HostFunctions>;
40

            
41
/// Generate a chain spec for use with the development service.
42
pub fn development_chain_spec(mnemonic: Option<String>, num_accounts: Option<u32>) -> ChainSpec {
43
	// Default mnemonic if none was provided
44
	let parent_mnemonic = mnemonic.unwrap_or_else(|| {
45
		"bottom drive obey lake curtain smoke basket hold race lonely fit walk".to_string()
46
	});
47
	let mut accounts = generate_accounts(parent_mnemonic, num_accounts.unwrap_or(10));
48
	// We add Gerald here
49
	accounts.push(AccountId::from(hex!(
50
		"6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b"
51
	)));
52

            
53
	ChainSpec::builder(
54
		WASM_BINARY.expect("WASM binary was not build, please build it!"),
55
		Extensions {
56
			relay_chain: "dev-service".into(),
57
			para_id: Default::default(),
58
		},
59
	)
60
	.with_name("Moonbeam Development Testnet")
61
	.with_id("moonbeam_dev")
62
	.with_chain_type(ChainType::Development)
63
	.with_properties(
64
		serde_json::from_str(
65
			"{\"tokenDecimals\": 18, \"tokenSymbol\": \"GLMR\", \"SS58Prefix\": 1284}",
66
		)
67
		.expect("Provided valid json map"),
68
	)
69
	.with_genesis_config(testnet_genesis(
70
		// Treasury Council members: Baltathar, Charleth and Dorothy
71
		vec![accounts[1], accounts[2], accounts[3]],
72
		// Open Tech committee members: Alith and Baltathar
73
		vec![accounts[0], accounts[1]],
74
		// Collator Candidate: Alice -> Alith
75
		vec![(
76
			accounts[0],
77
			get_from_seed::<NimbusId>("Alice"),
78
			20_000 * GLMR * SUPPLY_FACTOR,
79
		)],
80
		// Delegations
81
		vec![],
82
		accounts.clone(),
83
		1_500_000 * GLMR * SUPPLY_FACTOR,
84
		Default::default(), // para_id
85
		1281,               //ChainId
86
	))
87
	.build()
88
}
89

            
90
/// Generate a default spec for the parachain service. Use this as a starting point when launching
91
/// a custom chain.
92
pub fn get_chain_spec(para_id: ParaId) -> ChainSpec {
93
	ChainSpec::builder(
94
		WASM_BINARY.expect("WASM binary was not build, please build it!"),
95
		Extensions {
96
			relay_chain: "polkadot-local".into(),
97
			para_id: para_id.into(),
98
		},
99
	)
100
	// TODO Apps depends on this string to determine whether the chain is an ethereum compat
101
	// or not. We should decide the proper strings, and update Apps accordingly.
102
	// Or maybe Apps can be smart enough to say if the string contains "moonbeam" at all...
103
	.with_name("Moonbeam Local Testnet")
104
	.with_id("moonbeam_local")
105
	.with_chain_type(ChainType::Local)
106
	.with_properties(
107
		serde_json::from_str(
108
			"{\"tokenDecimals\": 18, \"tokenSymbol\": \"GLMR\", \"SS58Prefix\": 1284}",
109
		)
110
		.expect("Provided valid json map"),
111
	)
112
	.with_genesis_config(testnet_genesis(
113
		// Treasury Council members: Baltathar, Charleth and Dorothy
114
		vec![
115
			AccountId::from(hex!("3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0")),
116
			AccountId::from(hex!("798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc")),
117
			AccountId::from(hex!("773539d4Ac0e786233D90A233654ccEE26a613D9")),
118
		],
119
		// Open Tech committee members: Alith and Baltathar
120
		vec![
121
			AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")),
122
			AccountId::from(hex!("3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0")),
123
		],
124
		// Collator Candidates
125
		vec![
126
			// Alice -> Alith
127
			(
128
				AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")),
129
				get_from_seed::<NimbusId>("Alice"),
130
				20_000 * GLMR * SUPPLY_FACTOR,
131
			),
132
			// Bob -> Baltathar
133
			(
134
				AccountId::from(hex!("3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0")),
135
				get_from_seed::<NimbusId>("Bob"),
136
				20_000 * GLMR * SUPPLY_FACTOR,
137
			),
138
		],
139
		// Delegations
140
		vec![],
141
		// Endowed: Alith, Baltathar, Charleth and Dorothy
142
		vec![
143
			AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")),
144
			AccountId::from(hex!("3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0")),
145
			AccountId::from(hex!("798d4Ba9baf0064Ec19eB4F0a1a45785ae9D6DFc")),
146
			AccountId::from(hex!("773539d4Ac0e786233D90A233654ccEE26a613D9")),
147
		],
148
		1_500_000 * GLMR * SUPPLY_FACTOR,
149
		para_id,
150
		1280, //ChainId
151
	))
152
	.build()
153
}
154

            
155
#[cfg(test)]
156
mod tests {
157
	use super::*;
158
	#[test]
159
	fn test_derived_pairs_1() {
160
		let mnemonic =
161
			"bottom drive obey lake curtain smoke basket hold race lonely fit walk".to_string();
162
		let accounts = 10;
163
		let pairs = derive_bip44_pairs_from_mnemonic::<ecdsa::Public>(&mnemonic, accounts);
164
		let first_account = get_account_id_from_pair(pairs.first().unwrap().clone()).unwrap();
165
		let last_account = get_account_id_from_pair(pairs.last().unwrap().clone()).unwrap();
166

            
167
		let expected_first_account =
168
			AccountId::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac"));
169
		let expected_last_account =
170
			AccountId::from(hex!("2898FE7a42Be376C8BC7AF536A940F7Fd5aDd423"));
171
		assert_eq!(first_account, expected_first_account);
172
		assert_eq!(last_account, expected_last_account);
173
		assert_eq!(pairs.len(), 10);
174
	}
175
	#[test]
176
	fn test_derived_pairs_2() {
177
		let mnemonic =
178
			"slab nerve salon plastic filter inherit valve ozone crash thumb quality whale"
179
				.to_string();
180
		let accounts = 20;
181
		let pairs = derive_bip44_pairs_from_mnemonic::<ecdsa::Public>(&mnemonic, accounts);
182
		let first_account = get_account_id_from_pair(pairs.first().unwrap().clone()).unwrap();
183
		let last_account = get_account_id_from_pair(pairs.last().unwrap().clone()).unwrap();
184

            
185
		let expected_first_account =
186
			AccountId::from(hex!("1e56ca71b596f2b784a27a2fdffef053dbdeff83"));
187
		let expected_last_account =
188
			AccountId::from(hex!("4148202BF0c0Ad7697Cff87EbB83340C80c947f8"));
189
		assert_eq!(first_account, expected_first_account);
190
		assert_eq!(last_account, expected_last_account);
191
		assert_eq!(pairs.len(), 20);
192
	}
193
}