How to truncate a hash output and assign it to a new var?

I have this code:

use sha2::{Sha256, Digest};

fn main() {
    // Three integers to hash
    let a: u32 = 42;
    let b: u32 = 1024;
    let c: u32 = 65535;

    // Convert integers to bytes
    let mut hasher = Sha256::new();

    // Feed each integer as bytes to the hasher
    hasher.update(&a.to_le_bytes()); // Little-endian representation
    hasher.update(&b.to_le_bytes());
    hasher.update(&c.to_le_bytes());

    // Compute the hash
    let result = hasher.finalize();

    // Print the resulting hash in hexadecimal format
    println!("SHA-256 hash: {:x}", result);
}

An example print output for 'result` is 4127bcd2ae05466bc3e769fcf40e07a45cbe21264341fd9b26e6cd6ba87383ce

The aim is to take half of 'result' say 4127bcd2ae05466bc3e769fcf40e07a4 and use as below,

    let trun_hash = "4127bcd2ae05466bc3e769fcf40e07a4"; 	// trun_hash is the left half of 'result' 
    
    let num = u128::from_str_radix(&trun_hash[trun_hash.len() - 2..], 35).unwrap();
   
    println!("Hash converted to an integer: {}", num % 97);

How can I truncate the hash output as indicated above and immediately assign it to the var 'trun_hash' ?

I found this code here ... but there's a problem with types

fn truncate(s: &str, max_chars: isize) -> &str {
     match s.char_indices().nth(max_chars.try_into().unwrap()) {
         None => s,
         Some((idx, _)) => &s[..idx],
     }
 }

To get a u128 from the first half of the result hash:

let result_u128 = u128::from_be_bytes(result.as_slice()[..16].try_into().unwrap());

To turn it into a string:

let result_str = format!("{result_u128:x}");
1 Like

I used your first line here

    // Print the resulting hash in hexadecimal format
    println!("SHA-256 hash: {:x}", result);

    let result_u128 = u128::from_be_bytes(result.as_slice()[..16].try_into().unwrap());
    println!("Reduced hash to 128: {:x}", result_u128);

    // let hash_int = u128::from_str_radix(&result_u128[result_u128.len() - 2..], 35).unwrap();
    
    let trun_hash = "4127bcd2ae05466bc3e769fcf40e07a4";
   
    let num = u128::from_str_radix(&trun_hash[trun_hash.len() - 2..], 35).unwrap();
    // 0x8b % 4 == 139 % 4 == 3.
    println!("Hash converted to an integer: {}", num % 97);

... compiles correctly.
But if I uncomment the 'hash_int' declaration, I get this error

error[E0599]: no method named `len` found for type `u128` in the current scope

It must be a type thing ... because 'num' uses the same 'len' macro

result_u128 is a u128. The commented line assumes it's a string, for some reason? I also provided a line to write it as a string.

You can put the two together to get a trun_hash. It isn't terribly efficient.

    let result_u128 = u128::from_be_bytes(result.as_slice()[..16].try_into().unwrap());
    let trun_hash = format!("{result_u128:x}");

And I have no idea why you are treating the last two characters of the string as a base-35 encoded number. It's even more bizarre that you need a 128-bit integer to store it!

What I really want is an integer derived from any of the digits of the 'result' (e.g. the first 4127 or last 07a4 or anywhere in the middle).
So, 'result' being a hash digest, means the integer will be the same for different parties for as long as they pick the same digits of 'result' to compute the integer.

I wrote a program in Python that implements a small ZK proof system.
It requires both the prover and verifier to separately use a hash function to compute some integer c.
That's why I compute the hash and then derive some integer from the hash. This is literally 3 lines of code in Python :slight_smile:

    mssg = f'{b},{x},{s}'
    bxs_digest = sha256(mssg.encode('utf-8')).hexdigest()
    # Convert digest to integer value
    int_digest = np.array(int(bxs_digest, 16))

Maybe my original question should have been, "How to derive some small integer from a hash value in Rust?"

So, I am trying to convert the Python code to Rust.

Without more context for requirements, I can fill in the gaps and assume that 16-bits is "good enough" for your purpose, and suggest this:

// ...

// Compute the hash
let result = hasher.finalize();

// Get a small number from somewhere in the middle
let num = u16::from_be_bytes(result.as_slice()[14..16].try_into().unwrap());
println!("Small number from the middle: {num}");

You can do this numerically without a round-trip through string conversions. Even if you need more or less bits. 16 is easy because it aligns with the u16 type.

1 Like

This is perfect :star_struck:

Thank you so much @parasyte :smiling_face: