Any way I could optimize this code

use ethers::signers::{coins_bip39::English, MnemonicBuilder};
use eyre::Result;
use std::fs::OpenOptions;
use std::io::Write;
use bip39::Mnemonic;
use itertools::Itertools;

// Function to generate permutations
fn generate_permutations() -> Vec<Vec<String>> {
    let words = ["winter", "market", "lake", "easy", "dutch", "song", "rib", "goat", "parrot", "fork", "fiber", "fog"];
    let permutations = words.iter().permutations(words.len())
        .map(|perm| perm.into_iter().map(|&s| s.to_string()).collect::<Vec<String>>()) // map &str to String
        .filter(|perm| {
            perm[1] == "song"
                && perm[5] == "rib"
                && perm[6] == "market"
                && perm[10] == "winter"
                && perm.contains(&"lake".to_string())
        })
        .collect::<Vec<Vec<String>>>();
    permutations
}

// Function to check if permutations produce valid mnemonics
fn check_valid(permutations: Vec<Vec<String>>) -> Vec<String> {
    let mut valid_phrases = Vec::new();

    for perm in permutations {
        let mnemonic_str = perm.join(" ");
        if Mnemonic::parse_normalized(&mnemonic_str).is_ok() {
            valid_phrases.push(mnemonic_str);
        }
    }

    valid_phrases
}

// Function to check if a wallet contains the search string
fn contains_string(wallet: &str, search: &str) -> bool {
    wallet.contains(search)
}

fn main() -> Result<()> {
    let output_file_path = "ethers.txt";

    let mut output_file = OpenOptions::new()
        .write(true)
        .create(true)
        .truncate(true)
        .open(output_file_path)?;

    let index = 0u32;

    let permutations = generate_permutations();

    let valid_phrases = check_valid(permutations);

    for phrase in valid_phrases {
        // Build wallet from mnemonic phrase
        let wallet = MnemonicBuilder::<English>::default()
            .phrase(phrase.as_str())  // Convert String to &str
            .index(index)?
            .build()?;

        let wallet_address = format!("{:?}", wallet);

        let search_string = "496836a5cb";

        if contains_string(&wallet_address, search_string) {
            println!("True: {}", search_string);
            // Write the phrase and wallet address to the output file
            writeln!(output_file, "Phrase: {}, Wallet: {:?}", phrase, wallet)?;
        }
    }

    Ok(())
}

Any way I could optimize this code ? I think the bottlenecks are generating permutations with conditions and converting valid bip39 permutations
to ethereum addresses, so if there is another way to generate conditional permutations or a way to speed up this one and another way to convert
valid bip39 permutations to ethereum addresses(don't care about case-sensitive) or optimize this one, I would appreciate.

Not sure about the rest, but the following way of generationg permutations should be much faster and generate the same result:

    // Function to generate permutations
    fn generate_permutations_2() -> Vec<Vec<String>> {
        let words = ["lake", "easy", "dutch", "goat", "parrot", "fork", "fiber", "fog"];
        let permutations = words.iter().permutations(words.len())
            .map(|perm| perm.into_iter().map(|&s| s.to_string()).collect::<Vec<String>>()) // map &str to String
            .map(|mut perm| {
                perm.insert(1, "song".to_string());
                perm.insert(5, "rib".to_string());
                perm.insert(6, "market".to_string());
                perm.insert(10, "winter".to_string());
                perm
            })
            .collect::<Vec<Vec<String>>>();
        permutations
    }

since you require certain words be at certain positions, you can only permute the others and put those fixed ones onto their positions.

Also, I am not sure whether you actually need owned strings. In case you don't, you can avoid allocations by using only &'static str:

    fn generate_permutations_3() -> Vec<Vec<&'static str>> {
        let words = ["lake", "easy", "dutch", "goat", "parrot", "fork", "fiber", "fog"];
        let permutations = words.iter().permutations(words.len())
            .map(|perm| perm.into_iter().map(|x|*x).collect::<Vec<&'static str>>()) // map &str to String
            .map(|mut perm| {
                perm.insert(1, "song");
                perm.insert(5, "rib");
                perm.insert(6, "market");
                perm.insert(10, "winter");
                perm
            })
            .collect::<Vec<Vec<&'static str>>>();
        permutations
    }
2 Likes

Depending on how you want to use the items, it also might be cheaper to return an iterator, instead of allocating the outer Vec<_>:

use itertools::Itertools;

fn generate_permutations<'a, 'b: 'a>(words: &'a [&'b str]) -> impl Iterator<Item = Vec<&'b str>> + 'a {
    words
        .iter()
        .permutations(words.len())
        .map(|perm| perm.into_iter().map(|x| *x).collect::<Vec<_>>())
        .map(|mut perm| {
            perm.insert(1, "song");
            perm.insert(5, "rib");
            perm.insert(6, "market");
            perm.insert(10, "winter");
            perm
        })
}

fn main() {
    let words = [
        "lake", "easy", "dutch", "goat", "parrot", "fork", "fiber", "fog",
    ];
    
    for permutation in generate_permutations(&words) {
        println!("{permutation:?}");
    }
}
4 Likes

This worked for me, it sped up the code like 10x, I don't know how it skipped my mind to fix words at certain positions, also I changed to &'static str.
thank you.

If I wanted to generate permutations line by line(8 words in each line), 10 lines in total,
How do I go about it ? I have tried grouping them but not working out for me

I tested your code, it worked, thank you.
If I wanted to generate permutations line by line(8 words in each line), 10 lines in total,
How do I go about it ? I have tried grouping them but not working out for me

you can also use Arc<str> or Arc<String> instead of String.

By 8 words each line, I guess you mean the non-fixed positions? In that case there's no reason to insert the words at the fixed positions.

And if you want the first 10 out of the 40,320 permutations, you should definitely be using an iterator instead of eagerly generating all of the permutations.

Here's a breadcrumb.

1 Like

Yes, I mean the non fixed positions, if it checks one line, it goes down to the next line till the 10th line. My transition from python to Rust has not been an easy one, I read This guy's post and I loved it, I am also trying out a challenge myself, any way I could change his shift function to work in my code ?

Something like this, or should I read the words from a text file ?

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.