1use crate::{
9 error::{self, Error},
10 OutputType,
11};
12use serde_json::json;
13use std::path::PathBuf;
14use subsoil::core::{
15 crypto::{
16 unwrap_or_default_ss58_version, ExposeSecret, SecretString, Ss58AddressFormat, Ss58Codec,
17 Zeroize,
18 },
19 hexdisplay::HexDisplay,
20 Pair,
21};
22use subsoil::runtime::{traits::IdentifyAccount, MultiSigner};
23
24pub type PublicFor<P> = <P as subsoil::core::Pair>::Public;
26pub type SeedFor<P> = <P as subsoil::core::Pair>::Seed;
28
29pub fn read_uri(uri: Option<&String>) -> error::Result<String> {
31 let uri = if let Some(uri) = uri {
32 let file = PathBuf::from(&uri);
33 if file.is_file() {
34 std::fs::read_to_string(uri)?.trim_end().to_owned()
35 } else {
36 uri.into()
37 }
38 } else {
39 rpassword::prompt_password("URI: ")?
40 };
41
42 Ok(uri)
43}
44
45pub fn print_from_uri<Pair>(
55 uri: &str,
56 password: Option<SecretString>,
57 network_override: Option<Ss58AddressFormat>,
58 output: OutputType,
59) where
60 Pair: subsoil::core::Pair,
61 Pair::Public: Into<MultiSigner>,
62{
63 let password = password.as_ref().map(|s| s.expose_secret().as_str());
64 let network_id = String::from(unwrap_or_default_ss58_version(network_override));
65 if let Ok((pair, seed)) = Pair::from_phrase(uri, password) {
66 let public_key = pair.public();
67 let network_override = unwrap_or_default_ss58_version(network_override);
68
69 match output {
70 OutputType::Json => {
71 let json = json!({
72 "secretPhrase": uri,
73 "networkId": network_id,
74 "secretSeed": format_seed::<Pair>(seed),
75 "publicKey": format_public_key::<Pair>(public_key.clone()),
76 "ss58PublicKey": public_key.to_ss58check_with_version(network_override),
77 "accountId": format_account_id::<Pair>(public_key),
78 "ss58Address": pair.public().into().into_account().to_ss58check_with_version(network_override),
79 });
80 println!(
81 "{}",
82 serde_json::to_string_pretty(&json).expect("Json pretty print failed")
83 );
84 },
85 OutputType::Text => {
86 println!(
87 "Secret phrase: {}\n \
88 Network ID: {}\n \
89 Secret seed: {}\n \
90 Public key (hex): {}\n \
91 Account ID: {}\n \
92 Public key (SS58): {}\n \
93 SS58 Address: {}",
94 uri,
95 network_id,
96 format_seed::<Pair>(seed),
97 format_public_key::<Pair>(public_key.clone()),
98 format_account_id::<Pair>(public_key.clone()),
99 public_key.to_ss58check_with_version(network_override),
100 pair.public().into().into_account().to_ss58check_with_version(network_override),
101 );
102 },
103 }
104 } else if let Ok((pair, seed)) = Pair::from_string_with_seed(uri, password) {
105 let public_key = pair.public();
106 let network_override = unwrap_or_default_ss58_version(network_override);
107
108 match output {
109 OutputType::Json => {
110 let json = json!({
111 "secretKeyUri": uri,
112 "networkId": network_id,
113 "secretSeed": if let Some(seed) = seed { format_seed::<Pair>(seed) } else { "n/a".into() },
114 "publicKey": format_public_key::<Pair>(public_key.clone()),
115 "ss58PublicKey": public_key.to_ss58check_with_version(network_override),
116 "accountId": format_account_id::<Pair>(public_key),
117 "ss58Address": pair.public().into().into_account().to_ss58check_with_version(network_override),
118 });
119 println!(
120 "{}",
121 serde_json::to_string_pretty(&json).expect("Json pretty print failed")
122 );
123 },
124 OutputType::Text => {
125 println!(
126 "Secret Key URI `{}` is account:\n \
127 Network ID: {}\n \
128 Secret seed: {}\n \
129 Public key (hex): {}\n \
130 Account ID: {}\n \
131 Public key (SS58): {}\n \
132 SS58 Address: {}",
133 uri,
134 network_id,
135 if let Some(seed) = seed { format_seed::<Pair>(seed) } else { "n/a".into() },
136 format_public_key::<Pair>(public_key.clone()),
137 format_account_id::<Pair>(public_key.clone()),
138 public_key.to_ss58check_with_version(network_override),
139 pair.public().into().into_account().to_ss58check_with_version(network_override),
140 );
141 },
142 }
143 } else if let Ok((public_key, network)) = Pair::Public::from_string_with_version(uri) {
144 let network_override = network_override.unwrap_or(network);
145
146 match output {
147 OutputType::Json => {
148 let json = json!({
149 "publicKeyUri": uri,
150 "networkId": String::from(network_override),
151 "publicKey": format_public_key::<Pair>(public_key.clone()),
152 "accountId": format_account_id::<Pair>(public_key.clone()),
153 "ss58PublicKey": public_key.to_ss58check_with_version(network_override),
154 "ss58Address": public_key.to_ss58check_with_version(network_override),
155 });
156
157 println!(
158 "{}",
159 serde_json::to_string_pretty(&json).expect("Json pretty print failed")
160 );
161 },
162 OutputType::Text => {
163 println!(
164 "Public Key URI `{}` is account:\n \
165 Network ID/Version: {}\n \
166 Public key (hex): {}\n \
167 Account ID: {}\n \
168 Public key (SS58): {}\n \
169 SS58 Address: {}",
170 uri,
171 String::from(network_override),
172 format_public_key::<Pair>(public_key.clone()),
173 format_account_id::<Pair>(public_key.clone()),
174 public_key.to_ss58check_with_version(network_override),
175 public_key.to_ss58check_with_version(network_override),
176 );
177 },
178 }
179 } else {
180 println!("Invalid phrase/URI given");
181 }
182}
183
184pub fn print_from_public<Pair>(
186 public_str: &str,
187 network_override: Option<Ss58AddressFormat>,
188 output: OutputType,
189) -> Result<(), Error>
190where
191 Pair: subsoil::core::Pair,
192 Pair::Public: Into<MultiSigner>,
193{
194 let public = array_bytes::hex2bytes(public_str)?;
195
196 let public_key = Pair::Public::try_from(&public)
197 .map_err(|_| "Failed to construct public key from given hex")?;
198
199 let network_override = unwrap_or_default_ss58_version(network_override);
200
201 match output {
202 OutputType::Json => {
203 let json = json!({
204 "networkId": String::from(network_override),
205 "publicKey": format_public_key::<Pair>(public_key.clone()),
206 "accountId": format_account_id::<Pair>(public_key.clone()),
207 "ss58PublicKey": public_key.to_ss58check_with_version(network_override),
208 "ss58Address": public_key.to_ss58check_with_version(network_override),
209 });
210
211 println!("{}", serde_json::to_string_pretty(&json).expect("Json pretty print failed"));
212 },
213 OutputType::Text => {
214 println!(
215 "Network ID/Version: {}\n \
216 Public key (hex): {}\n \
217 Account ID: {}\n \
218 Public key (SS58): {}\n \
219 SS58 Address: {}",
220 String::from(network_override),
221 format_public_key::<Pair>(public_key.clone()),
222 format_account_id::<Pair>(public_key.clone()),
223 public_key.to_ss58check_with_version(network_override),
224 public_key.to_ss58check_with_version(network_override),
225 );
226 },
227 }
228
229 Ok(())
230}
231
232pub fn pair_from_suri<P: Pair>(suri: &str, password: Option<SecretString>) -> Result<P, Error> {
234 let result = if let Some(pass) = password {
235 let mut pass_str = pass.expose_secret().clone();
236 let pair = P::from_string(suri, Some(&pass_str));
237 pass_str.zeroize();
238 pair
239 } else {
240 P::from_string(suri, None)
241 };
242
243 Ok(result.map_err(|err| format!("Invalid phrase {:?}", err))?)
244}
245
246pub fn format_seed<P: subsoil::core::Pair>(seed: SeedFor<P>) -> String {
248 format!("0x{}", HexDisplay::from(&seed.as_ref()))
249}
250
251fn format_public_key<P: subsoil::core::Pair>(public_key: PublicFor<P>) -> String {
253 format!("0x{}", HexDisplay::from(&public_key.as_ref()))
254}
255
256fn format_account_id<P: subsoil::core::Pair>(public_key: PublicFor<P>) -> String
258where
259 PublicFor<P>: Into<MultiSigner>,
260{
261 format!("0x{}", HexDisplay::from(&public_key.into().into_account().as_ref()))
262}
263
264#[macro_export]
266macro_rules! with_crypto_scheme {
267 (
268 $scheme:expr,
269 $method:ident ( $($params:expr),* $(,)?) $(,)?
270 ) => {
271 $crate::with_crypto_scheme!($scheme, $method<>($($params),*))
272 };
273 (
274 $scheme:expr,
275 $method:ident<$($generics:ty),*>( $( $params:expr ),* $(,)?) $(,)?
276 ) => {
277 match $scheme {
278 $crate::CryptoScheme::Ecdsa => {
279 $method::<subsoil::core::ecdsa::Pair, $($generics),*>($($params),*)
280 }
281 $crate::CryptoScheme::Sr25519 => {
282 $method::<subsoil::core::sr25519::Pair, $($generics),*>($($params),*)
283 }
284 $crate::CryptoScheme::Ed25519 => {
285 $method::<subsoil::core::ed25519::Pair, $($generics),*>($($params),*)
286 }
287 }
288 };
289}