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
//! Moonbase 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 cumulus_primitives_core::ParaId;
26
use hex_literal::hex;
27
use moonbase_runtime::{
28
	currency::UNIT, genesis_config_preset::testnet_genesis, AccountId, WASM_BINARY,
29
};
30
use nimbus_primitives::NimbusId;
31
use sc_service::ChainType;
32
#[cfg(test)]
33
use sp_core::ecdsa;
34

            
35
/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type.
36
pub type ChainSpec = sc_service::GenericChainSpec<Extensions>;
37

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

            
50
2
	// Prefund the benchmark account for frontier, if compiling for benchmarks
51
2
	#[cfg(feature = "runtime-benchmarks")]
52
2
	accounts.push(AccountId::from(hex!(
53
2
		"1000000000000000000000000000000000000001"
54
2
	)));
55
2

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

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

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

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

            
192
		let expected_first_account =
193
			AccountId::from(hex!("1e56ca71b596f2b784a27a2fdffef053dbdeff83"));
194
		let expected_last_account =
195
			AccountId::from(hex!("4148202BF0c0Ad7697Cff87EbB83340C80c947f8"));
196
		assert_eq!(first_account, expected_first_account);
197
		assert_eq!(last_account, expected_last_account);
198
		assert_eq!(pairs.len(), 20);
199
	}
200
}