SHA256 hash bruteforcer
Run and address feedback from automated tools to get easy feedback without having to make use of another human's time. Possible tools include:


The Rust compiler. Don't turn off warnings.
Rustfmt is a tool for automatically formatting Rust code to the community-accepted style.
Clippy is a tool for finding common mistakes that may not be compilation errors but are unlikely to be what the programmer intended.


These detect things like:


incorrect / inconsistent indentation.
unneeded usages of return
non-idiomatic Rust naming 


Your variables have useless prefixes and suffixes. Don't blindly use Hungarian notation in a strongly typed language (e.g. nb). Dnt ndlssly abbv vrbs (don't needlessly abbreviate variables).

With this initial clearing of the underbrush, we can start to look at the code.


Don't use extern crate in modern Rust (What's the difference between use and extern?).
Don't use self::cratename, just use cratename.
I prefer to group all imports from the same crate into a single use statement.
Declare your variables as close as possible to where they are defined. Declare them inside of loops, especially if they aren't used outside of the loop or are reset inside the loop.
Use an infinite range instead of manually incrementing a variable.
Your function documentation is mostly useless. It's not using a doc comment (///). It repeats things like the function name. (How do you document function arguments?)
The type (String) has an unneeded parenthesis.
Your number formatting is grossly inefficient. You allocate many strings when you only need to extend an existing one. (What is the easiest way to pad a string with 0 to the left?; How can I append a formatted string to an existing String?)
Don't use _x if the variable is never used. Just use _.
Don't use magical values like "failed". Make use of Rust's Result type (What's the benefit of using a Result?)
There's no need to slice the string with [..]. Most of the time, a reference to a String will suffice (How does a reference to a String become a string slice?)
There's no need to return an owned String; a &amp;'static str will suffice (What are the differences between Rust's String and str?)
Avoid using as to convert numbers. Prefer From::from instead (How do I convert between numeric types safely and idiomatically?)
It's inefficient to convert the digest result to a string, slice the string, then convert back to a number. Perform the operations directly on the digest buffer. (How can I convert a buffer of a slice of bytes (&amp;[u8]) to an integer?; How to get a slice as an array in Rust?)
Don't use an i64 when you mean to use an u32
Using a match is cleaner than multiple if/else statements.


use crypto::{digest::Digest, sha2::Sha256};
use rand::Rng;
use std::{convert::TryInto, fs, process, time::Instant};

fn main() {
    // Local variables
    let winners = [
        "black", "green", "orange", "orange", "orange", "black", "black", "orange", "black",
        "black", "black", "orange", "black", "black", "orange", "orange", "green", "black",
        "orange", "black", "black", "orange", "orange", "black", "black", "black", "orange",
        "black", "black", "orange", "orange", "orange", "orange", "black", "orange", "black",
        "black", "orange", "orange", "black", "black", "orange", "green", "black", "orange",
        "orange", "orange", "orange", "green", "black",
    ];
    let public_seed = "2132110516";
    let round = 4_645_230;

    let now = Instant::now();

    for tries in 0.. {
        let random_seed = &amp;create_seed(32);
        let mut predict = predict_color(random_seed, public_seed, round).unwrap();
        let mut round_verifications = 0u8;

        // Check if the hash is correct by predicting the first 50 round of the day
        while predict == winners[usize::from(round_verifications)] {
            round_verifications += 1;
            predict = predict_color(random_seed, public_seed, round + u32::from(round_verifications))
                .unwrap();

            if round_verifications &gt; 45 {
                fs::write("seed.txt", random_seed).expect("Unable to write file");
                process::exit(1);
            }
        }

        if tries % 1_000_000 == 0 {
            println!("{}", tries);
            println!("In {} seconds", now.elapsed().as_secs());

            if tries == 5000000 { return }
        }
    }
}

/// Creates a random seed with a length of `2 * bytes`
fn create_seed(bytes: usize) -&gt; String {
    let mut rng = rand::thread_rng();
    let mut seed = String::with_capacity(2 * bytes);

    for _ in 0..bytes {
        use std::fmt::Write;
        write!(&amp;mut seed, "{:02x}", rng.gen::&lt;u8&gt;()).unwrap();
    }
    seed
}

/// Predict the roll
fn predict_color(
    server_seed: &amp;str,
    public_seed: &amp;str,
    first_round_of_the_day: u32,
) -&gt; Result&lt;&amp;'static str, ()&gt; {
    let mut hasher = Sha256::new();

    // Hash everything
    let to_hash = format!("{}-{}-{}", server_seed, public_seed, first_round_of_the_day);
    hasher.input_str(&amp;to_hash);

    let mut hash = [0; 32];
    hasher.result(&amp;mut hash);

    let leading_bytes: &amp;[u8; 4] = hash[..4].try_into().unwrap();
    let number = u32::from_be_bytes(*leading_bytes);

    match number % 15 {
        0 =&gt; Ok("green"),
        1..=7 =&gt; Ok("orange"),
        8..=14 =&gt; Ok("black"),
        _ =&gt; Err(()),
    }
}


Running this in release mode:

% time ./target/release/review
0
In 0 seconds
1000000
In 2 seconds
2000000
In 4 seconds
3000000
In 7 seconds
4000000
In 9 seconds
5000000
In 11 seconds

real    12.078  12077581us
user    11.879  11879005us
sys 0.033   33467us
cpu 98%
mem 968 KiB


Compared to your original:

% time ./target/release/review
1000000
In 14 secondes
2000000
In 28 secondes
3000000
In 42 secondes
4000000
In 56 secondes
5000000
In 70 secondes

real    1:10.49 70485131us
user    1:10.16 70162635us
sys 0.137   136869us
cpu 99%
mem 980 KiB


The improved version takes 17% of your original time (a 5.8x speed increase).



Using a random number generator for the seed is probably a bad idea. It's totally reasonable for a RNG to repeat a value, but that won't change your results. You might as well start at 0 and work your way up. This also makes it trivial to stop and resume.

The problem appears to be embarrassingly parallel, so that would also be an avenue to investigate.