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
use bip32::ExtendedPrivateKey;
17
use bip39::{Language, Mnemonic, Seed};
18
use libsecp256k1::{PublicKey, PublicKeyFormat};
19
use log::debug;
20
use moonbeam_cli_opt::account_key::Secp256k1SecretKey;
21
pub use moonbeam_core_primitives::AccountId;
22
use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup};
23
use serde::{Deserialize, Serialize};
24
use sha3::{Digest, Keccak256};
25
use sp_core::{ecdsa, Pair, Public, H160, H256};
26

            
27
pub mod fake_spec;
28
#[cfg(feature = "moonbase-native")]
29
pub mod moonbase;
30
#[cfg(feature = "moonbeam-native")]
31
pub mod moonbeam;
32
#[cfg(feature = "moonriver-native")]
33
pub mod moonriver;
34
#[cfg(feature = "moonbase-native")]
35
pub mod test_spec;
36

            
37
#[cfg(not(feature = "moonbase-native"))]
38
pub mod moonbase {
39
	pub type ChainSpec = crate::chain_spec::fake_spec::FakeSpec;
40
	pub fn chain_spec_from_json_file(_: std::path::PathBuf) -> Result<ChainSpec, String> {
41
		panic!("moonbase runtime not enabled")
42
	}
43
	pub fn development_chain_spec(_: Option<String>, _: Option<u32>) -> ChainSpec {
44
		panic!("moonbase runtime not enabled")
45
	}
46
}
47
#[cfg(not(feature = "moonriver-native"))]
48
pub mod moonriver {
49
	pub type ChainSpec = crate::chain_spec::fake_spec::FakeSpec;
50
	pub fn chain_spec_from_json_file(_: std::path::PathBuf) -> Result<ChainSpec, String> {
51
		panic!("moonriver runtime not enabled")
52
	}
53
	pub fn development_chain_spec(_: Option<String>, _: Option<u32>) -> ChainSpec {
54
		panic!("moonriver runtime not enabled")
55
	}
56
}
57
#[cfg(not(feature = "moonbeam-native"))]
58
pub mod moonbeam {
59
	pub type ChainSpec = crate::chain_spec::fake_spec::FakeSpec;
60
	pub fn chain_spec_from_json_file(_: std::path::PathBuf) -> Result<ChainSpec, String> {
61
		panic!("moonbeam runtime not enabled")
62
	}
63
	pub fn development_chain_spec(_: Option<String>, _: Option<u32>) -> ChainSpec {
64
		panic!("moonbeam runtime not enabled")
65
	}
66
}
67

            
68
pub type RawChainSpec = sc_service::GenericChainSpec<Extensions>;
69

            
70
2806
#[derive(Default, Clone, Serialize, Deserialize, ChainSpecExtension, ChainSpecGroup)]
71
#[serde(rename_all = "camelCase")]
72
pub struct Extensions {
73
	/// The relay chain of the Parachain.
74
	pub relay_chain: String,
75
	/// The id of the Parachain.
76
	pub para_id: u32,
77
}
78

            
79
impl Extensions {
80
	/// Try to get the extension from the given `ChainSpec`.
81
934
	pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> {
82
934
		sc_chain_spec::get_extension(chain_spec.extensions())
83
934
	}
84
}
85

            
86
/// Helper function to derive `num_accounts` child pairs from mnemonics
87
/// Substrate derive function cannot be used because the derivation is different than Ethereum's
88
/// https://substrate.dev/rustdocs/v2.0.0/src/sp_core/ecdsa.rs.html#460-470
89
2
pub fn derive_bip44_pairs_from_mnemonic<TPublic: Public>(
90
2
	mnemonic: &str,
91
2
	num_accounts: u32,
92
2
) -> Vec<TPublic::Pair> {
93
2
	let seed = Mnemonic::from_phrase(mnemonic, Language::English)
94
2
		.map(|x| Seed::new(&x, ""))
95
2
		.expect("Wrong mnemonic provided");
96
2

            
97
2
	let mut childs = Vec::new();
98
20
	for i in 0..num_accounts {
99
20
		if let Some(child_pair) = format!("m/44'/60'/0'/0/{}", i)
100
20
			.parse()
101
20
			.ok()
102
20
			.and_then(|derivation_path| {
103
20
				ExtendedPrivateKey::<Secp256k1SecretKey>::derive_from_path(&seed, &derivation_path)
104
20
					.ok()
105
20
			})
106
20
			.and_then(|extended| {
107
20
				TPublic::Pair::from_seed_slice(&extended.private_key().0.serialize()).ok()
108
20
			}) {
109
20
			childs.push(child_pair);
110
20
		} else {
111
			log::error!("An error ocurred while deriving key {} from parent", i)
112
		}
113
	}
114
2
	childs
115
2
}
116

            
117
/// Helper function to get an `AccountId` from an ECDSA Key Pair.
118
20
pub fn get_account_id_from_pair(pair: ecdsa::Pair) -> Option<AccountId> {
119
20
	let decompressed = PublicKey::parse_slice(&pair.public().0, Some(PublicKeyFormat::Compressed))
120
20
		.ok()?
121
20
		.serialize();
122
20

            
123
20
	let mut m = [0u8; 64];
124
20
	m.copy_from_slice(&decompressed[1..65]);
125
20

            
126
20
	Some(H160::from(H256::from_slice(Keccak256::digest(&m).as_slice())).into())
127
20
}
128

            
129
/// Function to generate accounts given a mnemonic and a number of child accounts to be generated
130
/// Defaults to a default mnemonic if no mnemonic is supplied
131
2
pub fn generate_accounts(mnemonic: String, num_accounts: u32) -> Vec<AccountId> {
132
2
	let childs = derive_bip44_pairs_from_mnemonic::<ecdsa::Public>(&mnemonic, num_accounts);
133
2
	debug!("Account Generation");
134
2
	childs
135
2
		.iter()
136
20
		.filter_map(|par| {
137
20
			let account = get_account_id_from_pair(par.clone());
138
20
			debug!(
139
				"private_key {} --------> Account {:x?}",
140
				sp_core::hexdisplay::HexDisplay::from(&par.clone().seed()),
141
				account
142
			);
143
20
			account
144
20
		})
145
2
		.collect()
146
2
}
147

            
148
/// Helper function to generate a crypto pair from seed
149
936
pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
150
936
	TPublic::Pair::from_string(&format!("//{}", seed), None)
151
936
		.expect("static values are valid; qed")
152
936
		.public()
153
936
}