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