moonbeam_cli_opt/
account_key.rs

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
17use bip32::{
18	Error as Bip32Error, ExtendedPrivateKey, PrivateKey as PrivateKeyT, PrivateKeyBytes,
19	PublicKey as PublicKeyT, PublicKeyBytes,
20};
21use bip39::{Language, Mnemonic, MnemonicType, Seed};
22use clap::{Parser, ValueEnum};
23use libsecp256k1::{PublicKey, SecretKey};
24use primitive_types::H256;
25use sp_runtime::traits::IdentifyAccount;
26
27#[derive(Debug, Clone, ValueEnum)]
28pub enum Network {
29	Moonbeam,
30	Moonriver,
31	Moonbase,
32	Ethereum,
33}
34
35impl Network {
36	/// Returns the coin type for the derivation path
37	pub fn coin_type(&self) -> u32 {
38		match self {
39			Network::Moonbeam => 1284,
40			Network::Moonriver => 1285,
41			Network::Moonbase => 1287,
42			Network::Ethereum => 60,
43		}
44	}
45}
46
47#[derive(Debug, Clone, Parser)]
48pub struct GenerateAccountKey {
49	/// Generate 12 words mnemonic instead of 24
50	#[clap(long, short = 'w')]
51	w12: bool,
52
53	/// Specify the mnemonic
54	#[clap(long, short = 'm')]
55	mnemonic: Option<String>,
56
57	/// The account index to use in the derivation path
58	#[clap(long = "account-index", short = 'a')]
59	account_index: Option<u32>,
60
61	/// The network to use for derivation path
62	#[clap(long, short = 'n', default_value = "moonbeam")]
63	pub network: Network,
64}
65
66impl GenerateAccountKey {
67	pub fn run(&self) {
68		// Retrieve the mnemonic from the args or generate random ones
69		let mnemonic = if let Some(phrase) = &self.mnemonic {
70			Mnemonic::from_phrase(phrase, Language::English).expect("invalid mnemonic")
71		} else {
72			match self.w12 {
73				true => Mnemonic::new(MnemonicType::Words12, Language::English),
74				false => Mnemonic::new(MnemonicType::Words24, Language::English),
75			}
76		};
77
78		// Retrieves the seed from the mnemonic
79		let seed = Seed::new(&mnemonic, "");
80		// Use network parameter to determine chain ID for derivation path
81		let derivation_path = format!(
82			"m/44'/{}'/0'/0/{}",
83			self.network.coin_type(),
84			self.account_index.unwrap_or(0)
85		);
86		let private_key = if let Some(private_key) =
87			derivation_path.parse().ok().and_then(|derivation_path| {
88				let extended = ExtendedPrivateKey::<Secp256k1SecretKey>::derive_from_path(
89					&seed,
90					&derivation_path,
91				)
92				.expect("invalid extended private key");
93				Some(extended.private_key().0)
94			}) {
95			private_key
96		} else {
97			panic!("invalid extended private key");
98		};
99
100		// Retrieves the public key
101		let public_key = PublicKey::from_secret_key(&private_key);
102
103		// Convert into Ethereum-style address.
104		let signer: account::EthereumSigner = public_key.into();
105		let address = signer.into_account();
106
107		println!("Address:      {:?}", address);
108		println!("Mnemonic:     {}", mnemonic.phrase());
109		println!("Private Key:  {:?}", H256::from(private_key.serialize()));
110		println!("Path:         {}", derivation_path);
111	}
112}
113
114// `libsecp256k1::PublicKey` wrapped type
115pub struct Secp256k1PublicKey(pub PublicKey);
116// `libsecp256k1::Secret`  wrapped type
117pub struct Secp256k1SecretKey(pub SecretKey);
118
119impl PublicKeyT for Secp256k1PublicKey {
120	fn from_bytes(bytes: PublicKeyBytes) -> Result<Self, Bip32Error> {
121		let public = PublicKey::parse_compressed(&bytes).map_err(|_| return Bip32Error::Decode)?;
122		Ok(Self(public))
123	}
124
125	fn to_bytes(&self) -> PublicKeyBytes {
126		self.0.serialize_compressed()
127	}
128
129	fn derive_child(&self, other: PrivateKeyBytes) -> Result<Self, Bip32Error> {
130		let mut child = self.0.clone();
131		let secret = SecretKey::parse(&other).map_err(|_| return Bip32Error::Decode)?;
132		let _ = child.tweak_add_assign(&secret);
133		Ok(Self(child))
134	}
135}
136
137impl PrivateKeyT for Secp256k1SecretKey {
138	type PublicKey = Secp256k1PublicKey;
139
140	fn from_bytes(bytes: &PrivateKeyBytes) -> Result<Self, Bip32Error> {
141		let secret = SecretKey::parse(&bytes).map_err(|_| return Bip32Error::Decode)?;
142		Ok(Self(secret))
143	}
144
145	fn to_bytes(&self) -> PrivateKeyBytes {
146		self.0.serialize()
147	}
148
149	fn derive_child(&self, other: PrivateKeyBytes) -> Result<Self, Bip32Error> {
150		let mut child = self.0.clone();
151		let secret = SecretKey::parse(&other).map_err(|_| return Bip32Error::Decode)?;
152		let _ = child.tweak_add_assign(&secret);
153		Ok(Self(child))
154	}
155
156	fn public_key(&self) -> Self::PublicKey {
157		Secp256k1PublicKey(PublicKey::from_secret_key(&self.0))
158	}
159}