1use crate::helpers::args::{network_id_parser, parse_private_key};
17
18mod sign;
19use sign::Sign;
20
21use snarkvm::console::{
22 account::{Address, PrivateKey, Signature},
23 network::{CanaryV0, MainnetV0, Network, TestnetV0},
24 prelude::{Environment, Uniform},
25 program::{ToFields, Value},
26 types::Field,
27};
28
29use anyhow::{Result, anyhow, bail};
30use clap::Parser;
31use colored::Colorize;
32use core::str::FromStr;
33use crossterm::ExecutableCommand;
34use rand::SeedableRng;
35use rand_chacha::ChaChaRng;
36use rayon::prelude::*;
37use std::{
38 fs::File,
39 io::{Read, Write},
40};
41
42use zeroize::Zeroize;
43
44#[derive(Debug, Parser, Zeroize)]
46#[command(
47 rename_all = "kebab-case",
50)]
51pub enum Account {
52 New {
54 #[clap(long, default_value_t=MainnetV0::ID, long, value_parser = network_id_parser())]
57 network: u16,
58 #[clap(short = 's', long)]
60 seed: Option<String>,
61 #[clap(short = 'v', long)]
63 vanity: Option<String>,
64 #[clap(long)]
66 discreet: bool,
67 #[clap(long)]
69 save_to_file: Option<String>,
70 },
71 Sign(Sign),
72 Verify {
73 #[clap(long, default_value_t=MainnetV0::ID, long, value_parser = network_id_parser())]
76 network: u16,
77 #[clap(short = 'a', long)]
79 address: String,
80 #[clap(short = 's', long)]
82 signature: String,
83 #[clap(short = 'm', long)]
85 message: String,
86 #[clap(short = 'r', long)]
88 raw: bool,
89 },
90}
91
92fn aleo_literal_to_fields<N: Network>(input: &str) -> Result<Vec<Field<N>>> {
94 Value::<N>::from_str(input)?.to_fields()
95}
96
97impl Account {
98 pub fn parse(self) -> Result<String> {
100 match self {
101 Self::New { network, seed, vanity, discreet, save_to_file } => {
102 if seed.is_some() && vanity.is_some() {
104 bail!("Cannot specify both the '--seed' and '--vanity' flags");
105 }
106
107 if save_to_file.is_some() && vanity.is_some() {
108 bail!("Cannot specify both the '--save-to-file' and '--vanity' flags");
109 }
110
111 if save_to_file.is_some() && discreet {
112 bail!("Cannot specify both the '--save-to-file' and '--discreet' flags");
113 }
114
115 match vanity {
116 Some(vanity) => match network {
118 MainnetV0::ID => Self::new_vanity::<MainnetV0>(vanity.as_str(), discreet),
119 TestnetV0::ID => Self::new_vanity::<TestnetV0>(vanity.as_str(), discreet),
120 CanaryV0::ID => Self::new_vanity::<CanaryV0>(vanity.as_str(), discreet),
121 unknown_id => bail!("Unknown network ID ({unknown_id})"),
122 },
123 None => match network {
125 MainnetV0::ID => Self::new_seeded::<MainnetV0>(seed, discreet, save_to_file),
126 TestnetV0::ID => Self::new_seeded::<TestnetV0>(seed, discreet, save_to_file),
127 CanaryV0::ID => Self::new_seeded::<CanaryV0>(seed, discreet, save_to_file),
128 unknown_id => bail!("Unknown network ID ({unknown_id})"),
129 },
130 }
131 }
132 Self::Sign(sign) => sign.execute(),
133
134 Self::Verify { network, address, signature, message, raw } => {
135 match network {
137 MainnetV0::ID => Self::verify::<MainnetV0>(address, signature, message, raw),
138 TestnetV0::ID => Self::verify::<TestnetV0>(address, signature, message, raw),
139 CanaryV0::ID => Self::verify::<CanaryV0>(address, signature, message, raw),
140 unknown_id => bail!("Unknown network ID ({unknown_id})"),
141 }
142 }
143 }
144 }
145
146 fn new_vanity<N: Network>(vanity: &str, discreet: bool) -> Result<String> {
148 let sample_account = || snarkos_account::Account::<N>::new(&mut rand::thread_rng());
150
151 const ITERATIONS: u128 = u16::MAX as u128;
152 const ITERATIONS_STR: &str = "65,535";
153
154 if !crate::helpers::is_in_bech32m_charset(vanity) {
156 bail!(
157 "The vanity string '{vanity}' contains invalid bech32m characters. Try using characters from the bech32m character set: {}",
158 crate::helpers::BECH32M_CHARSET
159 );
160 }
161
162 if vanity.len() > 4 {
164 let message =
165 format!(" The vanity string '{vanity}' contains 5 or more characters and will take a while to find.\n");
166 println!("{}", message.yellow());
167 }
168
169 loop {
170 let timer = std::time::Instant::now();
172
173 let account = (0..ITERATIONS).into_par_iter().find_map_any(|_| {
176 let mut account = None;
178 if let Ok(candidate) = sample_account() {
180 let address = candidate.address().to_string();
182 if crate::helpers::has_vanity_string(&address, vanity) {
185 account = Some(candidate);
186 }
187 }
188 account
190 });
191
192 if let Some(account) = account {
194 println!(); if !discreet {
196 return Ok(account.to_string());
197 }
198 display_string_discreetly(
199 &format!("{:>12} {}", "Private Key".cyan().bold(), account.private_key()),
200 "### Do not share or lose this private key! Press any key to complete. ###",
201 )
202 .unwrap();
203 let account_info = format!(
204 " {:>12} {}\n {:>12} {}",
205 "View Key".cyan().bold(),
206 account.view_key(),
207 "Address".cyan().bold(),
208 account.address()
209 );
210 return Ok(account_info);
211 } else {
212 let rate = ITERATIONS / timer.elapsed().as_millis();
213 let rate = format!("[{rate} a/ms]");
214 println!(" {} Sampled {ITERATIONS_STR} accounts, searching...", rate.dimmed());
215 }
216 }
217 }
218
219 fn new_seeded<N: Network>(seed: Option<String>, discreet: bool, save_to_file: Option<String>) -> Result<String> {
221 let seed = match seed {
223 Some(seed) => {
225 Field::new(<N as Environment>::Field::from_str(&seed).map_err(|e| anyhow!("Invalid seed - {e}"))?)
226 }
227 None => Field::rand(&mut ChaChaRng::from_entropy()),
229 };
230 let private_key =
232 PrivateKey::try_from(seed).map_err(|_| anyhow!("Failed to convert the seed into a valid private key"))?;
233 let account = snarkos_account::Account::<N>::try_from(private_key)?;
235 if let Some(path) = save_to_file {
237 crate::check_parent_permissions(&path)?;
238 let mut file = File::create_new(path)?;
239 file.write_all(account.private_key().to_string().as_bytes())?;
240 crate::set_user_read_only(&file)?;
241 }
242 if !discreet {
244 return Ok(account.to_string());
245 }
246 display_string_discreetly(
247 &format!("{:>12} {}", "Private Key".cyan().bold(), account.private_key()),
248 "### Do not share or lose this private key! Press any key to complete. ###",
249 )
250 .unwrap();
251 let account_info = format!(
252 " {:>12} {}\n {:>12} {}",
253 "View Key".cyan().bold(),
254 account.view_key(),
255 "Address".cyan().bold(),
256 account.address()
257 );
258 Ok(account_info)
259 }
260
261 fn verify<N: Network>(address: String, signature: String, message: String, raw: bool) -> Result<String> {
263 let address = Address::<N>::from_str(&address).map_err(|_| anyhow!("Failed to parse a valid address"))?;
265 let signature =
267 Signature::<N>::from_str(&signature).map_err(|_| anyhow!("Failed to parse a valid signature"))?;
268 let verified = if raw {
270 signature.verify_bytes(&address, message.as_bytes())
271 } else {
272 let fields =
273 aleo_literal_to_fields(&message).map_err(|_| anyhow!("Failed to parse a valid Aleo literal"))?;
274 signature.verify(&address, &fields)
275 };
276
277 match verified {
279 true => Ok("✅ The signature is valid".to_string()),
280 false => bail!("❌ The signature is invalid"),
281 }
282 }
283}
284
285fn display_string_discreetly(discreet_string: &str, continue_message: &str) -> Result<()> {
287 use crossterm::{
288 style::Print,
289 terminal::{EnterAlternateScreen, LeaveAlternateScreen},
290 };
291 let mut stdout = std::io::stdout();
292 stdout.execute(EnterAlternateScreen)?;
293 stdout.execute(Print(format!("{discreet_string}\n{continue_message}")))?;
295 stdout.flush()?;
296 wait_for_keypress();
297 stdout.execute(LeaveAlternateScreen)?;
298 Ok(())
299}
300
301fn wait_for_keypress() {
302 let mut single_key = [0u8];
303 std::io::stdin().read_exact(&mut single_key).unwrap();
304}
305
306#[cfg(test)]
307mod tests {
308 use super::{Account, Sign};
309
310 use std::{fs, fs::Permissions, io::Write};
311 use tempfile::{NamedTempFile, TempDir};
312
313 use anyhow::{Context, Result};
314 use colored::Colorize;
315
316 #[test]
317 fn test_new() -> Result<()> {
318 for _ in 0..3 {
319 let account = Account::New { network: 0, seed: None, vanity: None, discreet: false, save_to_file: None };
320 account.parse().with_context(|| "Account creation failed")?;
321 }
322
323 Ok(())
324 }
325
326 #[test]
327 fn test_new_seeded() -> Result<()> {
328 let seed = Some(1231275789u64.to_string());
329
330 let mut expected = format!(
331 " {:>12} {}\n",
332 "Private Key".cyan().bold(),
333 "APrivateKey1zkp2n22c19hNdGF8wuEoQcuiyuWbquY6up4CtG5DYKqPX2X"
334 );
335 expected += &format!(
336 " {:>12} {}\n",
337 "View Key".cyan().bold(),
338 "AViewKey1pNxZHn79XVJ4D2WG5Vn2YWsAzf5wzAs3dAuQtUAmUFF7"
339 );
340 expected += &format!(
341 " {:>12} {}",
342 "Address".cyan().bold(),
343 "aleo1uxl69laseuv3876ksh8k0nd7tvpgjt6ccrgccedpjk9qwyfensxst9ftg5"
344 );
345
346 let vanity = None;
347 let account = Account::New { network: 0, seed, vanity, discreet: false, save_to_file: None };
348 let actual = account.parse().with_context(|| "Command execution failed")?;
349 assert_eq!(expected, actual);
350
351 Ok(())
352 }
353
354 #[test]
355 fn test_new_seeded_with_256bits_input() -> anyhow::Result<()> {
356 let seed = Some("38868010450269069756484274649022187108349082664538872491798902858296683054657".to_string());
357
358 let mut expected = format!(
359 " {:>12} {}\n",
360 "Private Key".cyan().bold(),
361 "APrivateKey1zkp61PAYmrYEKLtRWeWhUoDpFnGLNuHrCciSqN49T86dw3p"
362 );
363 expected += &format!(
364 " {:>12} {}\n",
365 "View Key".cyan().bold(),
366 "AViewKey1eYEGtb78FVg38SSYyzAeXnBdnWCba5t5YxUxtkTtvNAE"
367 );
368 expected += &format!(
369 " {:>12} {}",
370 "Address".cyan().bold(),
371 "aleo1zecnqchckrzw7dlsyf65g6z5le2rmys403ecwmcafrag0e030yxqrnlg8j"
372 );
373
374 let vanity = None;
375 let account = Account::New { network: 0, seed, vanity, discreet: false, save_to_file: None };
376
377 let actual = account.parse().with_context(|| "Command execution failed")?;
378 assert_eq!(expected, actual);
379
380 Ok(())
381 }
382
383 #[cfg(unix)]
384 #[test]
385 fn test_new_save_to_file() -> anyhow::Result<()> {
386 use std::os::unix::fs::PermissionsExt;
387
388 let dir = TempDir::new().expect("Failed to create temp folder");
389 let dir_path = dir.path();
390 fs::set_permissions(dir_path, Permissions::from_mode(0o700)).expect("Failed to set permissions");
391
392 let mut file = dir.path().to_owned();
393 file.push("my-private-key-file");
394 let file = file.display().to_string();
395
396 let seed = Some(1231275789u64.to_string());
397 let vanity = None;
398 let discreet = false;
399 let save_to_file = Some(file.clone());
400 let account = Account::New { network: 0, seed, vanity, discreet, save_to_file };
401
402 let actual = account.parse().with_context(|| "Command execution failed")?;
403
404 let expected = "APrivateKey1zkp2n22c19hNdGF8wuEoQcuiyuWbquY6up4CtG5DYKqPX2X";
405 assert!(actual.contains(expected));
406
407 let content = fs::read_to_string(&file).expect("Failed to read private-key-file");
408 assert_eq!(expected, content);
409
410 let metadata = fs::metadata(file).unwrap();
412 let permissions = metadata.permissions();
413 assert_eq!(permissions.mode() & 0o777, 0o400, "File permissions are not 0o400");
414
415 Ok(())
416 }
417
418 #[cfg(unix)]
419 #[test]
420 fn test_new_prevent_save_to_file_in_non_protected_folder() {
421 use std::os::unix::fs::PermissionsExt;
422
423 let dir = TempDir::new().expect("Failed to create temp folder");
424 let dir_path = dir.path();
425 fs::set_permissions(dir_path, Permissions::from_mode(0o444)).expect("Failed to set permissions");
426
427 let mut file = dir.path().to_owned();
428 file.push("my-private-key-file");
429 let file = file.display().to_string();
430
431 let seed = None;
432 let vanity = None;
433 let discreet = false;
434 let save_to_file = Some(file);
435 let account = Account::New { network: 0, seed, vanity, discreet, save_to_file };
436
437 let res = account.parse();
438 assert!(res.is_err());
439 }
440
441 #[test]
442 fn test_new_prevent_save_to_file_in_non_existing_folder() {
443 let dir = TempDir::new().expect("Failed to create temp folder");
444
445 let mut file = dir.path().to_owned();
446 file.push("missing-folder");
447 file.push("my-private-key-file");
448 let file = file.display().to_string();
449
450 let seed = None;
451 let vanity = None;
452 let discreet = false;
453 let save_to_file = Some(file);
454 let account = Account::New { network: 0, seed, vanity, discreet, save_to_file };
455
456 let res = account.parse();
457 assert!(res.is_err());
458 }
459
460 #[test]
461 fn test_new_prevent_overwrite_existing_file() {
462 let mut file = NamedTempFile::new().expect("Failed to create temp file");
463 write!(file, "don't overwrite me").expect("Failed to write secret to file");
464
465 let seed = None;
466 let vanity = None;
467 let discreet = false;
468 let path = file.path().display().to_string();
469 let account = Account::New { network: 0, seed, vanity, discreet, save_to_file: Some(path) };
470
471 let res = account.parse();
472 assert!(res.is_err());
473
474 let expected = "don't overwrite me";
475 let content = fs::read_to_string(file).expect("Failed to read private-key-file");
476 assert_eq!(expected, content);
477 }
478
479 #[test]
480 fn test_new_disallow_save_to_file_with_discreet() {
481 let seed = None;
482 let vanity = None;
483 let discreet = true;
484 let save_to_file = Some("/tmp/not-important".to_string());
485 let account = Account::New { network: 0, seed, vanity, discreet, save_to_file };
486
487 let res = account.parse();
488 assert!(res.is_err());
489 }
490
491 #[test]
492 fn test_new_disallow_save_to_file_with_vanity() {
493 let seed = None;
494 let vanity = Some("foo".to_string());
495 let discreet = false;
496 let save_to_file = Some("/tmp/not-important".to_string());
497 let account = Account::New { network: 0, seed, vanity, discreet, save_to_file };
498
499 let res = account.parse();
500 assert!(res.is_err());
501 }
502
503 #[test]
504 fn test_signature_raw() -> Result<()> {
505 let key = "APrivateKey1zkp61PAYmrYEKLtRWeWhUoDpFnGLNuHrCciSqN49T86dw3p".to_string();
506 let message = "Hello, world!".to_string();
507 let account = Account::Sign(Sign {
508 network: 0,
509 private_key: Some(key),
510 private_key_file: None,
511 message,
512 raw: true,
513 dev_key: None,
514 });
515
516 account.parse().with_context(|| "Command execution failed")?;
517 Ok(())
518 }
519
520 #[test]
521 fn test_signature_raw_using_private_key_file() -> Result<()> {
522 let key = "APrivateKey1zkp61PAYmrYEKLtRWeWhUoDpFnGLNuHrCciSqN49T86dw3p".to_string();
523 let message = "Hello, world!".to_string();
524
525 let mut file = NamedTempFile::new().expect("Failed to create temp file");
526 writeln!(file, "{key}").expect("Failed to write key to temp file");
527
528 let path = file.path().display().to_string();
529 let account = Account::Sign(Sign {
530 network: 0,
531 private_key: None,
532 private_key_file: Some(path),
533 message,
534 raw: true,
535 dev_key: None,
536 });
537
538 account.parse().with_context(|| "Command execution failed")?;
539 Ok(())
540 }
541
542 #[cfg(unix)]
543 #[test]
544 fn test_signature_raw_using_private_key_file_from_account_new() -> Result<()> {
545 use std::os::unix::fs::PermissionsExt;
546
547 let message = "Hello, world!".to_string();
548
549 let dir = TempDir::new().expect("Failed to create temp folder");
550 let dir_path = dir.path();
551 fs::set_permissions(dir_path, Permissions::from_mode(0o700)).expect("Failed to set permissions");
552
553 let mut file = dir.path().to_owned();
554 file.push("my-private-key-file");
555 let file = file.display().to_string();
556
557 let seed = None;
558 let vanity = None;
559 let discreet = false;
560 let account = Account::New { network: 0, seed, vanity, discreet, save_to_file: Some(file.clone()) };
561 account.parse().with_context(|| "Command execution failed")?;
562
563 let account = Account::Sign(Sign {
564 network: 0,
565 private_key: None,
566 private_key_file: Some(file),
567 message,
568 raw: true,
569 dev_key: None,
570 });
571
572 account.parse().with_context(|| "Command execution failed")?;
573 Ok(())
574 }
575
576 #[test]
577 fn test_signature() -> Result<()> {
578 let key = "APrivateKey1zkp61PAYmrYEKLtRWeWhUoDpFnGLNuHrCciSqN49T86dw3p".to_string();
579 let message = "5field".to_string();
580 let account = Account::Sign(Sign {
581 network: 0,
582 private_key: Some(key),
583 private_key_file: None,
584 message,
585 raw: false,
586 dev_key: None,
587 });
588
589 account.parse().with_context(|| "Command execution failed")?;
590 Ok(())
591 }
592
593 #[test]
594 fn test_signature_fail() {
595 let key = "APrivateKey1zkp61PAYmrYEKLtRWeWhUoDpFnGLNuHrCciSqN49T86dw3p".to_string();
596 let message = "not a literal value".to_string();
597 let account = Account::Sign(Sign {
598 network: 0,
599 private_key: Some(key),
600 private_key_file: None,
601 message,
602 raw: false,
603 dev_key: None,
604 });
605 assert!(account.parse().is_err());
606 }
607
608 #[test]
609 fn test_verify_raw() -> Result<()> {
610 let address = "aleo1zecnqchckrzw7dlsyf65g6z5le2rmys403ecwmcafrag0e030yxqrnlg8j";
612 let signature = "sign1nnvrjlksrkxdpwsrw8kztjukzhmuhe5zf3srk38h7g32u4kqtqpxn3j5a6k8zrqcfx580a96956nsjvluzt64cqf54pdka9mgksfqp8esm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qkwsnaqq".to_string();
613 let message = "Hello, world!".to_string();
614 let account = Account::Verify { network: 0, address: address.to_string(), signature, message, raw: true };
615
616 account.parse().with_context(|| "Command execution failed")?;
617
618 let signature = "sign1nnvrjlksrkxdpwsrw8kztjukzhmuhe5zf3srk38h7g32u4kqtqpxn3j5a6k8zrqcfx580a96956nsjvluzt64cqf54pdka9mgksfqp8esm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qkwsnaqq".to_string();
620 let message = "Different Message".to_string();
621 let account = Account::Verify { network: 0, address: address.to_string(), signature, message, raw: true };
622 let actual = account.parse();
623 assert!(actual.is_err());
624
625 let signature = "sign1nnvrjlksrkxdpwsrw8kztjukzhmuhe5zf3srk38h7g32u4kqtqpxn3j5a6k8zrqcfx580a96956nsjvluzt64cqf54pdka9mgksfqp8esm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qkwsnaqq".to_string();
627 let message = "Hello, world!".to_string();
628 let wrong_address = "aleo1uxl69laseuv3876ksh8k0nd7tvpgjt6ccrgccedpjk9qwyfensxst9ftg5".to_string();
629 let account = Account::Verify { network: 0, address: wrong_address, signature, message, raw: true };
630 let actual = account.parse();
631 assert!(actual.is_err());
632
633 let signature = "sign1424ztyt9hcm77nq450gvdszrvtg9kvhc4qadg4nzy9y0ah7wdqq7t36cxal42p9jj8e8pjpmc06lfev9nvffcpqv0cxwyr0a2j2tjqlesm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qk3yrr50".to_string();
635 let message = "Different Message".to_string();
636 let account = Account::Verify { network: 0, address: address.to_string(), signature, message, raw: true };
637 account.parse().with_context(|| "Command execution failed")?;
638
639 Ok(())
640 }
641
642 #[test]
643 fn test_verify() -> Result<()> {
644 let address = "aleo1zecnqchckrzw7dlsyf65g6z5le2rmys403ecwmcafrag0e030yxqrnlg8j";
646 let signature = "sign1j7swjfnyujt2vme3ulu88wdyh2ddj85arh64qh6c6khvrx8wvsp8z9wtzde0sahqj2qwz8rgzt803c0ceega53l4hks2mf5sfsv36qhesm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qkdetews".to_string();
647 let message = "5field".to_string();
648 let account = Account::Verify { network: 0, address: address.to_string(), signature, message, raw: false };
649 account.parse().with_context(|| "Command execution failed")?;
650
651 let signature = "sign1j7swjfnyujt2vme3ulu88wdyh2ddj85arh64qh6c6khvrx8wvsp8z9wtzde0sahqj2qwz8rgzt803c0ceega53l4hks2mf5sfsv36qhesm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qkdetews".to_string();
653 let message = "10field".to_string();
654 let account = Account::Verify { network: 0, address: address.to_string(), signature, message, raw: false };
655 let actual = account.parse();
656 assert!(actual.is_err());
657
658 let signature = "sign1j7swjfnyujt2vme3ulu88wdyh2ddj85arh64qh6c6khvrx8wvsp8z9wtzde0sahqj2qwz8rgzt803c0ceega53l4hks2mf5sfsv36qhesm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qkdetews".to_string();
660 let message = "5field".to_string();
661 let wrong_address = "aleo1uxl69laseuv3876ksh8k0nd7tvpgjt6ccrgccedpjk9qwyfensxst9ftg5".to_string();
662 let account = Account::Verify { network: 0, address: wrong_address, signature, message, raw: false };
663 let actual = account.parse();
664 assert!(actual.is_err());
665
666 let signature = "sign1t9v2t5tljk8pr5t6vkcqgkus0a3v69vryxmfrtwrwg0xtj7yv5qj2nz59e5zcyl50w23lhntxvt6vzeqfyu6dt56698zvfj2l6lz6q0esm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qk8rh9kt".to_string();
668 let message = "10field".to_string();
669 let account = Account::Verify { network: 0, address: address.to_string(), signature, message, raw: false };
670
671 account.parse().with_context(|| "Command execution failed")?;
672
673 Ok(())
674 }
675}