alloy_zksync/network/unsigned_tx/eip712/
utils.rs1use k256::sha2::{self, Digest};
2
3const WORD_SIZE: usize = 32;
5const MAX_BYTECODE_LENGTH: usize = WORD_SIZE * u16::MAX as usize;
6
7#[derive(Debug, thiserror::Error)]
9pub enum BytecodeHashError {
10 #[error("Bytecode cannot be split into 32-byte words")]
11 BytecodeNotAligned,
12 #[error(
13 "Bytecode length exceeds limit: {num_words} words, the maximum is {MAX_BYTECODE_LENGTH}"
14 )]
15 BytecodeLengthExceedsLimit { num_words: usize },
16 #[error("Bytecode must have odd number of words")]
17 NumberOfWordsMustBeOdd,
18}
19
20pub fn hash_bytecode(bytecode: &[u8]) -> Result<[u8; 32], BytecodeHashError> {
31 if bytecode.len() % WORD_SIZE != 0 {
32 return Err(BytecodeHashError::BytecodeNotAligned);
33 }
34
35 let bytecode_length = bytecode.len() / WORD_SIZE;
36 let bytecode_length = u16::try_from(bytecode_length).map_err(|_| {
37 BytecodeHashError::BytecodeLengthExceedsLimit {
38 num_words: bytecode_length,
39 }
40 })?;
41 if bytecode_length % 2 == 0 {
42 return Err(BytecodeHashError::NumberOfWordsMustBeOdd);
43 }
44
45 let bytecode_hash: [u8; 32] = sha2::Sha256::digest(bytecode).into();
46
47 let mut contract_hash: [u8; 32] = [0u8; 32];
48 contract_hash[..2].copy_from_slice(&0x0100_u16.to_be_bytes());
49 contract_hash[2..4].copy_from_slice(&bytecode_length.to_be_bytes());
50 contract_hash[4..].copy_from_slice(&bytecode_hash[4..]);
51
52 Ok(contract_hash)
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58 use assert_matches::assert_matches;
59
60 #[test]
61 fn bytecode_hash() {
62 #[rustfmt::skip]
64 let test_vector = [
65 (vec![10u8; 32], hex::decode("01000001e7718454476f04edeb935022ae4f4d90934ab7ce913ff20c8baeb399").unwrap()),
66 (vec![20u8; 96], hex::decode("01000003c743f1d99f4d7dc11f5d9630e32ff5a212c5aaf64c7ac815193463d4").unwrap()),
67 ];
68
69 for (input, expected) in test_vector.iter() {
70 let hash = hash_bytecode(input).unwrap();
71 assert_eq!(&hash[..], &expected[..]);
72 }
73
74 assert_matches!(
75 hash_bytecode(&[]),
76 Err(BytecodeHashError::NumberOfWordsMustBeOdd)
77 );
78 assert_matches!(
79 hash_bytecode(&[1]),
80 Err(BytecodeHashError::BytecodeNotAligned)
81 );
82 }
83}