#[cfg(test)]
use miden_protocol::Word;
use miden_protocol::account::StorageMapKey;
use miden_protocol::utils::{bytes_to_packed_u32_elements, packed_u32_elements_to_bytes};
use miden_protocol::{Felt, Hasher};
use super::error::EncodingError;
pub fn bytes32_to_storage_map_key(b: &[u8; 32]) -> StorageMapKey {
let felts = bytes32_to_packed_felts(b);
StorageMapKey::new(Hasher::hash_elements(&felts))
}
pub fn bytes32_to_packed_felts(b: &[u8; 32]) -> [Felt; 8] {
bytes_to_packed_u32_elements(b)
.try_into()
.expect("32 bytes always pack to exactly 8 u32 felts")
}
#[cfg(test)]
pub fn bytes32_to_word_lossless(b: &[u8; 32]) -> Result<Word, EncodingError> {
Word::try_from(*b).map_err(|_| EncodingError::LimbOutOfField)
}
pub fn packed_felts_to_bytes32(felts: &[Felt; 8]) -> Result<[u8; 32], EncodingError> {
for f in felts {
if f.as_canonical_u64() > u32::MAX as u64 {
return Err(EncodingError::LimbNotU32);
}
}
Ok(packed_u32_elements_to_bytes(felts)
.try_into()
.expect("8 u32 limbs always unpack to exactly 32 bytes"))
}
#[cfg(test)]
mod tests {
use assert_matches::assert_matches;
use super::*;
use crate::vectors::{load, word_from_hex};
#[test]
fn tv_b32_1_hash_to_word_positive() {
let v = load();
for vec in v.families.b32.iter().filter(|v| v.lossless_error.is_none()) {
let key = bytes32_to_storage_map_key(&vec.bytes32());
assert_eq!(
Word::from(key),
word_from_hex(&vec.expected_key),
"vector {}: key mismatch",
vec.id
);
}
}
#[test]
fn tv_b32_2_lossless_rejects_option_b_succeeds() {
let v = load();
let vec = v
.families
.b32
.iter()
.find(|v| v.lossless_error.is_some())
.expect("artifact must carry the limb-ge-p vector");
assert_matches!(
bytes32_to_word_lossless(&vec.bytes32()),
Err(EncodingError::LimbOutOfField),
"vector {}: lossless path must reject",
vec.id
);
let key = bytes32_to_storage_map_key(&vec.bytes32());
assert_eq!(Word::from(key), word_from_hex(&vec.expected_key), "vector {}", vec.id);
}
#[test]
fn tv_b32_3_determinism() {
let v = load();
for vec in &v.families.b32 {
let a = bytes32_to_storage_map_key(&vec.bytes32());
let b = bytes32_to_storage_map_key(&vec.bytes32());
assert_eq!(a, b, "vector {}: keying must be deterministic", vec.id);
}
}
#[test]
fn tv_b32_4_packing_is_8_felts_two_words() {
let v = load();
for vec in &v.families.b32 {
let felts = bytes32_to_packed_felts(&vec.bytes32());
assert_eq!(felts.len(), 8, "vector {}: packing width", vec.id);
let expected: Vec<Felt> = vec.packed_felts_values();
assert_eq!(felts.as_slice(), expected.as_slice(), "vector {}: limbs", vec.id);
}
}
#[test]
fn tv_b32_inverse_round_trip() {
let v = load();
for vec in &v.families.b32 {
let b = vec.bytes32();
let felts = bytes32_to_packed_felts(&b);
let back = packed_felts_to_bytes32(&felts).expect("valid u32 limbs round-trip");
assert_eq!(back, b, "vector {}: inverse round-trip", vec.id);
assert_eq!(bytes32_to_packed_felts(&back), felts, "vector {}: forward∘inverse", vec.id);
}
}
#[test]
fn tv_b32_inverse_rejects_non_u32() {
let mut felts = [Felt::from(0u32); 8];
felts[3] = Felt::try_from((u32::MAX as u64) + 1).expect("2^32 < p");
assert_matches!(
packed_felts_to_bytes32(&felts),
Err(EncodingError::LimbNotU32),
"a 2^32 limb must be rejected, not truncated"
);
}
}