1use {
2 crate::config::ExecutionCost,
3 blake3::Hasher as Blake3Hasher,
4 sbpf_vm::{
5 compute::ComputeMeter,
6 errors::{SbpfVmError, SbpfVmResult},
7 memory::Memory,
8 },
9 sha2::Sha256,
10 sha3::Keccak256,
11};
12
13trait Hasher {
14 fn new() -> Self;
15 fn update(&mut self, data: &[u8]);
16 fn finalize(self) -> Vec<u8>;
17}
18
19impl Hasher for Sha256 {
20 fn new() -> Self {
21 sha2::Digest::new()
22 }
23 fn update(&mut self, data: &[u8]) {
24 sha2::Digest::update(self, data);
25 }
26 fn finalize(self) -> Vec<u8> {
27 sha2::Digest::finalize(self).to_vec()
28 }
29}
30
31impl Hasher for Keccak256 {
32 fn new() -> Self {
33 sha3::Digest::new()
34 }
35 fn update(&mut self, data: &[u8]) {
36 sha3::Digest::update(self, data);
37 }
38 fn finalize(self) -> Vec<u8> {
39 sha3::Digest::finalize(self).to_vec()
40 }
41}
42
43impl Hasher for Blake3Hasher {
44 fn new() -> Self {
45 blake3::Hasher::new()
46 }
47 fn update(&mut self, data: &[u8]) {
48 blake3::Hasher::update(self, data);
49 }
50 fn finalize(self) -> Vec<u8> {
51 blake3::Hasher::finalize(&self).as_bytes().to_vec()
52 }
53}
54
55fn read_slices(memory: &Memory, vals_addr: u64, vals_len: u64) -> SbpfVmResult<Vec<(u64, u64)>> {
56 let mut slices = Vec::with_capacity(vals_len as usize);
57 for i in 0..vals_len {
58 let slice_addr = vals_addr.saturating_add(i.saturating_mul(16));
59 let ptr = memory.read_u64(slice_addr)?;
60 let len = memory.read_u64(slice_addr.saturating_add(8))?;
61 slices.push((ptr, len));
62 }
63 Ok(slices)
64}
65
66fn hash_slices<H: Hasher>(
67 memory: &mut Memory,
68 compute: &ComputeMeter,
69 costs: &ExecutionCost,
70 vals_addr: u64,
71 vals_len: u64,
72 result_addr: u64,
73) -> SbpfVmResult<u64> {
74 if vals_len > costs.sha256_max_slices {
75 return Err(SbpfVmError::TooManySlices);
76 }
77
78 compute.consume(costs.sha256_base_cost)?;
79
80 let mut hasher = H::new();
81 if vals_len > 0 {
82 for (ptr, len) in read_slices(memory, vals_addr, vals_len)? {
83 let cost = costs
84 .mem_op_base_cost
85 .max(costs.sha256_byte_cost.saturating_mul(len / 2));
86 compute.consume(cost)?;
87 hasher.update(memory.read_bytes(ptr, len as usize)?);
88 }
89 }
90
91 memory.write_bytes(result_addr, &hasher.finalize())?;
92 Ok(0)
93}
94
95pub fn sol_sha256(
96 registers: [u64; 5],
97 memory: &mut Memory,
98 compute: &ComputeMeter,
99 costs: &ExecutionCost,
100) -> SbpfVmResult<u64> {
101 hash_slices::<Sha256>(
102 memory,
103 compute,
104 costs,
105 registers[0],
106 registers[1],
107 registers[2],
108 )
109}
110
111pub fn sol_keccak256(
112 registers: [u64; 5],
113 memory: &mut Memory,
114 compute: &ComputeMeter,
115 costs: &ExecutionCost,
116) -> SbpfVmResult<u64> {
117 hash_slices::<Keccak256>(
118 memory,
119 compute,
120 costs,
121 registers[0],
122 registers[1],
123 registers[2],
124 )
125}
126
127pub fn sol_blake3(
128 registers: [u64; 5],
129 memory: &mut Memory,
130 compute: &ComputeMeter,
131 costs: &ExecutionCost,
132) -> SbpfVmResult<u64> {
133 hash_slices::<Blake3Hasher>(
134 memory,
135 compute,
136 costs,
137 registers[0],
138 registers[1],
139 registers[2],
140 )
141}
142
143#[cfg(test)]
144mod tests {
145 use {
146 super::*,
147 crate::syscalls::tests::test_helpers::{costs, make_memory, meter},
148 sbpf_vm::{errors::SbpfVmError, memory::Memory},
149 };
150
151 fn setup_single_slice(memory: &mut Memory, data: &[u8]) -> (u64, u64) {
152 let data_addr = Memory::HEAP_START;
153 memory.write_bytes(data_addr, data).unwrap();
154
155 let slices_addr = Memory::HEAP_START + 64;
156 memory.write_u64(slices_addr, data_addr).unwrap();
157 memory
158 .write_u64(slices_addr + 8, data.len() as u64)
159 .unwrap();
160
161 let result_addr = Memory::HEAP_START + 128;
162 (slices_addr, result_addr)
163 }
164
165 fn reference_sha256(data: &[u8]) -> Vec<u8> {
166 use sha2::Digest;
167 sha2::Sha256::digest(data).to_vec()
168 }
169
170 fn reference_keccak256(data: &[u8]) -> Vec<u8> {
171 use sha3::Digest;
172 sha3::Keccak256::digest(data).to_vec()
173 }
174
175 fn reference_blake3(data: &[u8]) -> Vec<u8> {
176 blake3::hash(data).as_bytes().to_vec()
177 }
178
179 #[test]
180 fn test_sha256_known_input() {
181 let mut memory = make_memory();
182 let (slices_addr, result_addr) = setup_single_slice(&mut memory, b"hello");
183
184 let registers = [slices_addr, 1, result_addr, 0, 0];
185 sol_sha256(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
186
187 let result = memory.read_bytes(result_addr, 32).unwrap();
188 assert_eq!(result, reference_sha256(b"hello").as_slice());
189 }
190
191 #[test]
192 fn test_sha256_empty_slices() {
193 let mut memory = make_memory();
194 let result_addr = Memory::HEAP_START + 128;
195
196 let registers = [0, 0, result_addr, 0, 0];
197 sol_sha256(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
198
199 let result = memory.read_bytes(result_addr, 32).unwrap();
200 assert_eq!(result, reference_sha256(b"").as_slice());
201 }
202
203 #[test]
204 fn test_sha256_multiple_slices_concatenated() {
205 let mut memory = make_memory();
206 memory.write_bytes(Memory::HEAP_START, b"he").unwrap();
207 memory.write_bytes(Memory::HEAP_START + 8, b"llo").unwrap();
208
209 let slices_addr = Memory::HEAP_START + 64;
210 memory.write_u64(slices_addr, Memory::HEAP_START).unwrap();
211 memory.write_u64(slices_addr + 8, 2).unwrap();
212 memory
213 .write_u64(slices_addr + 16, Memory::HEAP_START + 8)
214 .unwrap();
215 memory.write_u64(slices_addr + 24, 3).unwrap();
216
217 let result_addr = Memory::HEAP_START + 128;
218 let registers = [slices_addr, 2, result_addr, 0, 0];
219 sol_sha256(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
220
221 let result = memory.read_bytes(result_addr, 32).unwrap();
222 assert_eq!(result, reference_sha256(b"hello").as_slice());
223 }
224
225 #[test]
226 fn test_sha256_too_many_slices() {
227 let mut memory = make_memory();
228 let result_addr = Memory::HEAP_START + 128;
229 let registers = [Memory::HEAP_START, 20_001, result_addr, 0, 0];
230 assert!(matches!(
231 sol_sha256(registers, &mut memory, &meter(1_000_000), &costs()),
232 Err(SbpfVmError::TooManySlices)
233 ));
234 }
235
236 #[test]
237 fn test_sha256_compute_exhausted() {
238 let mut memory = make_memory();
239 let result_addr = Memory::HEAP_START + 128;
240 let registers = [0, 0, result_addr, 0, 0];
241 assert!(matches!(
242 sol_sha256(registers, &mut memory, &meter(84), &costs()),
243 Err(SbpfVmError::ComputeBudgetExceeded { .. })
244 ));
245 }
246
247 #[test]
248 fn test_keccak256_known_input() {
249 let mut memory = make_memory();
250 let (slices_addr, result_addr) = setup_single_slice(&mut memory, b"hello");
251
252 let registers = [slices_addr, 1, result_addr, 0, 0];
253 sol_keccak256(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
254
255 let result = memory.read_bytes(result_addr, 32).unwrap();
256 assert_eq!(result, reference_keccak256(b"hello").as_slice());
257 }
258
259 #[test]
260 fn test_keccak256_empty_slices() {
261 let mut memory = make_memory();
262 let result_addr = Memory::HEAP_START + 128;
263 let registers = [0, 0, result_addr, 0, 0];
264 sol_keccak256(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
265
266 let result = memory.read_bytes(result_addr, 32).unwrap();
267 assert_eq!(result, reference_keccak256(b"").as_slice());
268 }
269
270 #[test]
271 fn test_keccak256_differs_from_sha256() {
272 let mut memory = make_memory();
273 let (slices_addr, result_addr) = setup_single_slice(&mut memory, b"hello");
274
275 let registers = [slices_addr, 1, result_addr, 0, 0];
276 sol_keccak256(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
277
278 let keccak_result = memory.read_bytes(result_addr, 32).unwrap().to_vec();
279 assert_ne!(keccak_result, reference_sha256(b"hello"));
280 }
281
282 #[test]
283 fn test_blake3_known_input() {
284 let mut memory = make_memory();
285 let (slices_addr, result_addr) = setup_single_slice(&mut memory, b"hello");
286
287 let registers = [slices_addr, 1, result_addr, 0, 0];
288 sol_blake3(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
289
290 let result = memory.read_bytes(result_addr, 32).unwrap();
291 assert_eq!(result, reference_blake3(b"hello").as_slice());
292 }
293
294 #[test]
295 fn test_blake3_empty_slices() {
296 let mut memory = make_memory();
297 let result_addr = Memory::HEAP_START + 128;
298 let registers = [0, 0, result_addr, 0, 0];
299 sol_blake3(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
300
301 let result = memory.read_bytes(result_addr, 32).unwrap();
302 assert_eq!(result, reference_blake3(b"").as_slice());
303 }
304
305 #[test]
306 fn test_all_three_hashes_differ_on_same_input() {
307 let mut memory = make_memory();
308
309 let (slices_addr, result_addr) = setup_single_slice(&mut memory, b"test");
310 let registers = [slices_addr, 1, result_addr, 0, 0];
311
312 sol_sha256(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
313 let sha_out = memory.read_bytes(result_addr, 32).unwrap().to_vec();
314
315 sol_keccak256(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
316 let keccak_out = memory.read_bytes(result_addr, 32).unwrap().to_vec();
317
318 sol_blake3(registers, &mut memory, &meter(1_000_000), &costs()).unwrap();
319 let blake_out = memory.read_bytes(result_addr, 32).unwrap().to_vec();
320
321 assert_ne!(sha_out, keccak_out);
322 assert_ne!(sha_out, blake_out);
323 assert_ne!(keccak_out, blake_out);
324 }
325}