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
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
Thank you so much @parasyte