miden_core_lib/handlers/
sha512.rs1use alloc::{format, vec, vec::Vec};
9use core::array;
10
11use miden_core::{
12 Felt, Word, ZERO,
13 crypto::hash::{Poseidon2, Sha512},
14 events::EventName,
15 precompile::{PrecompileCommitment, PrecompileError, PrecompileRequest, PrecompileVerifier},
16 utils::bytes_to_packed_u32_elements,
17};
18use miden_processor::{
19 ProcessorState,
20 advice::AdviceMutation,
21 event::{EventError, EventHandler},
22};
23
24use crate::handlers::{BYTES_PER_U32, read_memory_packed_u32};
25
26pub const SHA512_HASH_BYTES_EVENT_NAME: EventName =
28 EventName::new("miden::core::hash::sha512::hash_bytes");
29
30pub struct Sha512Precompile;
31
32impl EventHandler for Sha512Precompile {
33 fn on_event(&self, process: &ProcessorState) -> Result<Vec<AdviceMutation>, EventError> {
42 let ptr = process.get_stack_item(1).as_canonical_u64();
44 let len_bytes = process.get_stack_item(2).as_canonical_u64();
45
46 let max = process.execution_options().max_hash_len_bytes();
49 if len_bytes > max as u64 {
50 return Err(format!(
51 "sha512 input length {len_bytes} bytes exceeds maximum of {max} bytes"
52 )
53 .into());
54 }
55 let len_bytes = usize::try_from(len_bytes).map_err(|_| {
56 EventError::from(format!("sha512 input length {len_bytes} exceeds addressable range"))
57 })?;
58
59 let input_bytes = read_memory_packed_u32(process, ptr, len_bytes)?;
61 let preimage = Sha512Preimage::new(input_bytes);
62 let digest = preimage.digest();
63
64 Ok(vec![
65 AdviceMutation::extend_stack(digest.0),
66 AdviceMutation::extend_precompile_requests([preimage.into()]),
67 ])
68 }
69}
70
71impl PrecompileVerifier for Sha512Precompile {
72 fn verify(&self, calldata: &[u8]) -> Result<PrecompileCommitment, PrecompileError> {
73 let preimage = Sha512Preimage::new(calldata.to_vec());
74 Ok(preimage.precompile_commitment())
75 }
76}
77
78#[derive(Debug, Copy, Clone, Eq, PartialEq)]
83pub struct Sha512FeltDigest(pub [Felt; 16]);
84
85impl Sha512FeltDigest {
86 pub fn from_bytes(bytes: &[u8]) -> Self {
87 assert_eq!(bytes.len(), 64, "digest must be 64 bytes");
88 let packed: [u32; 16] = array::from_fn(|i| {
89 let limbs = array::from_fn(|j| bytes[BYTES_PER_U32 * i + j]);
90 u32::from_le_bytes(limbs)
91 });
92 Self(packed.map(Felt::from_u32))
93 }
94
95 pub fn to_commitment(&self) -> Word {
96 Poseidon2::hash_elements(&self.0)
97 }
98}
99
100impl AsRef<[Felt]> for Sha512FeltDigest {
101 fn as_ref(&self) -> &[Felt] {
102 &self.0
103 }
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
111pub struct Sha512Preimage(Vec<u8>);
112
113impl Sha512Preimage {
114 pub fn new(bytes: Vec<u8>) -> Self {
115 Self(bytes)
116 }
117
118 pub fn into_inner(self) -> Vec<u8> {
119 self.0
120 }
121
122 pub fn as_felts(&self) -> Vec<Felt> {
123 bytes_to_packed_u32_elements(self.as_ref())
124 }
125
126 pub fn input_commitment(&self) -> Word {
127 Poseidon2::hash_elements(&self.as_felts())
128 }
129
130 pub fn digest(&self) -> Sha512FeltDigest {
131 let hash = Sha512::hash(self.as_ref());
132 Sha512FeltDigest::from_bytes(&hash)
133 }
134
135 pub fn precompile_commitment(&self) -> PrecompileCommitment {
136 let tag = self.precompile_tag();
137 let comm = Poseidon2::merge(&[self.input_commitment(), self.digest().to_commitment()]);
138 PrecompileCommitment::new(tag, comm)
139 }
140
141 fn precompile_tag(&self) -> Word {
142 [
143 SHA512_HASH_BYTES_EVENT_NAME.to_event_id().as_felt(),
144 Felt::new(self.as_ref().len() as u64),
145 ZERO,
146 ZERO,
147 ]
148 .into()
149 }
150}
151
152impl AsRef<[u8]> for Sha512Preimage {
153 fn as_ref(&self) -> &[u8] {
154 &self.0
155 }
156}
157
158impl From<Sha512Preimage> for PrecompileRequest {
159 fn from(preimage: Sha512Preimage) -> Self {
160 PrecompileRequest::new(SHA512_HASH_BYTES_EVENT_NAME.to_event_id(), preimage.into_inner())
161 }
162}