1use crate::{
6 alloc::{Allocator, Global, vec::Vec},
7 codec::{Decode, DecodeIn, Decoder, Encode, Encoder},
8 error::{DecodeError, EncodeError},
9};
10use alloy_primitives::hex;
11use core::{fmt, hint::unreachable_unchecked};
12use digest::Digest;
13use ripemd::Ripemd160;
14use sha1::Sha1;
15use sha2::Sha256;
16use sha3::Keccak256;
17
18#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
22#[cfg_attr(
23 feature = "serde",
24 derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
25)]
26#[repr(transparent)]
27pub struct OpCode(u8);
28
29impl fmt::Debug for OpCode {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 f.write_str(self.name())
32 }
33}
34
35impl fmt::Display for OpCode {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 f.write_str(self.name())
39 }
40}
41
42impl Encode for OpCode {
43 #[inline]
44 fn encode(&self, encoder: &mut impl Encoder) -> Result<(), EncodeError> {
45 encoder.encode_byte(self.tag())
46 }
47}
48
49impl Decode for OpCode {
50 #[inline]
51 fn decode(decoder: &mut impl Decoder) -> Result<Self, DecodeError> {
52 let byte = decoder.decode_byte()?;
53 OpCode::new(byte).ok_or(DecodeError::BadOpCode(byte))
54 }
55}
56
57impl OpCode {
58 #[inline]
60 pub const fn tag(self) -> u8 {
61 self.0
62 }
63
64 #[inline]
66 pub const fn has_immediate(&self) -> bool {
67 matches!(*self, Self::APPEND | Self::PREPEND)
68 }
69
70 #[inline]
72 pub const fn is_control(&self) -> bool {
73 matches!(*self, Self::ATTESTATION | Self::FORK)
74 }
75
76 #[inline]
78 pub const fn is_digest(&self) -> bool {
79 self.as_digest().is_some()
80 }
81
82 #[inline]
84 pub const fn as_digest(&self) -> Option<DigestOp> {
85 match *self {
86 Self::SHA1 | Self::SHA256 | Self::RIPEMD160 | Self::KECCAK256 => Some(DigestOp(*self)),
87 _ => None,
88 }
89 }
90
91 #[inline]
97 pub fn execute(&self, input: impl AsRef<[u8]>, immediate: impl AsRef<[u8]>) -> Vec<u8> {
98 self.execute_in(input, immediate, Global)
99 }
100
101 #[inline]
107 pub fn execute_in<A: Allocator>(
108 &self,
109 input: impl AsRef<[u8]>,
110 immediate: impl AsRef<[u8]>,
111 alloc: A,
112 ) -> Vec<u8, A> {
113 if let Some(digest_op) = self.as_digest() {
114 return digest_op.execute_in(input, alloc);
115 }
116
117 let input = input.as_ref();
118 match *self {
119 Self::APPEND => {
120 let immediate = immediate.as_ref();
121 let mut out = Vec::with_capacity_in(input.len() + immediate.len(), alloc);
122 out.extend_from_slice(input);
123 out.extend_from_slice(immediate);
124 out
125 }
126 Self::PREPEND => {
127 let immediate = immediate.as_ref();
128 let mut out = Vec::with_capacity_in(input.len() + immediate.len(), alloc);
129 out.extend_from_slice(immediate);
130 out.extend_from_slice(input);
131 out
132 }
133 Self::REVERSE => {
134 let len = input.len();
135 let mut out = Vec::<u8, A>::with_capacity_in(len, alloc);
136
137 unsafe {
138 out.set_len(len);
140
141 let input_ptr = input.as_ptr();
143 let out_ptr = out.as_mut_ptr();
144 for i in 0..len {
145 *out_ptr.add(i) = *input_ptr.add(len - 1 - i);
147 }
148 }
149 out
150 }
151 Self::HEXLIFY => {
152 let hex_len = input.len() * 2;
153 let mut out = Vec::<u8, A>::with_capacity_in(hex_len, alloc);
154 unsafe {
156 out.set_len(hex_len);
157 }
158 unsafe {
160 hex::encode_to_slice(input, &mut out).unwrap_unchecked();
161 }
162 out
163 }
164 _ => panic!("Cannot execute control opcode"),
165 }
166 }
167}
168
169impl PartialEq<u8> for OpCode {
170 fn eq(&self, other: &u8) -> bool {
171 self.tag().eq(other)
172 }
173}
174
175#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
179#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
180#[repr(transparent)]
181pub struct DigestOp(OpCode);
182
183impl fmt::Debug for DigestOp {
184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185 f.write_str(self.0.name())
186 }
187}
188
189impl fmt::Display for DigestOp {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 write!(f, "{}", self.0)
192 }
193}
194
195impl PartialEq<OpCode> for DigestOp {
196 fn eq(&self, other: &OpCode) -> bool {
197 self.0.eq(other)
198 }
199}
200
201impl PartialEq<u8> for DigestOp {
202 fn eq(&self, other: &u8) -> bool {
203 self.0.eq(other)
204 }
205}
206
207impl Encode for DigestOp {
208 #[inline]
209 fn encode(&self, encoder: &mut impl Encoder) -> Result<(), EncodeError> {
210 self.0.encode(encoder)
211 }
212}
213
214impl<A: Allocator> DecodeIn<A> for DigestOp {
215 #[inline]
216 fn decode_in(decoder: &mut impl Decoder, _alloc: A) -> Result<Self, DecodeError> {
217 let opcode = OpCode::decode(decoder)?;
218 opcode
219 .as_digest()
220 .ok_or(DecodeError::ExpectedDigestOp(opcode))
221 }
222}
223
224impl DigestOp {
225 #[inline]
227 pub const fn to_opcode(self) -> OpCode {
228 self.0
229 }
230
231 #[inline]
233 pub const fn tag(&self) -> u8 {
234 self.0.tag()
235 }
236}
237
238pub trait DigestOpExt: Digest {
240 const OPCODE: DigestOp;
241
242 fn opcode() -> DigestOp;
243}
244
245macro_rules! define_opcodes {
246 ($($val:literal => $variant:ident),* $(,)?) => {
247 $(
248 #[doc = concat!("The `", stringify!($val), "` (\"", stringify!($variant),"\") opcode.")]
249 pub const $variant: u8 = $val;
250 )*
251
252 $(
253 impl OpCode {
254 #[doc = concat!("The `", stringify!($val), "` (\"", stringify!($variant),"\") opcode.")]
255 pub const $variant: Self = Self($val);
256 }
257 )*
258
259 impl OpCode {
260 #[inline]
261 pub const fn new(v: u8) -> Option<Self> {
262 match v {
263 $( $val => Some(Self::$variant), )*
264 _ => None,
265 }
266 }
267
268 #[inline]
269 pub const fn name(&self) -> &'static str {
270 match *self {
271 $( Self::$variant => stringify!($variant), )*
272 _ => unsafe { unreachable_unchecked() }
274 }
275 }
276 }
277
278 #[derive(Debug)]
280 pub struct OpCodeFromStrError;
281
282 impl core::fmt::Display for OpCodeFromStrError {
283 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
284 write!(f, "invalid opcode string")
285 }
286 }
287
288 impl std::error::Error for OpCodeFromStrError {}
289
290 impl core::str::FromStr for OpCode {
291 type Err = OpCodeFromStrError;
292
293 #[inline]
294 fn from_str(s: &str) -> Result<Self, Self::Err> {
295 match s {
296 $( stringify!($variant) => Ok(Self::$variant), )*
297 _ => Err(OpCodeFromStrError),
298 }
299 }
300 }
301 };
302}
303
304macro_rules! define_digest_opcodes {
305 ($($val:literal => $variant:ident),* $(,)?) => {
306 $(
307 impl DigestOp {
308 #[doc = concat!("The `", stringify!($val), "` (\"", stringify!($variant),"\") digest opcode.")]
309 pub const $variant: Self = Self(OpCode::$variant);
310 }
311 )*
312
313 impl DigestOp {
314 #[inline]
316 pub const fn output_size(&self) -> usize {
317 use digest::typenum::Unsigned;
318 paste::paste! {
319 match *self {
320 $( Self::$variant => <[<$variant:camel>] as ::digest::OutputSizeUser>::OutputSize::USIZE, )*
321 _ => unsafe { unreachable_unchecked() }
323 }
324 }
325 }
326
327 pub fn execute(&self, input: impl AsRef<[u8]>) -> $crate::alloc::vec::Vec<u8> {
329 self.execute_in(input, $crate::alloc::Global)
330 }
331
332 pub fn execute_in<A: $crate::alloc::Allocator>(&self, input: impl AsRef<[u8]>, alloc: A) -> $crate::alloc::vec::Vec<u8, A> {
334 match *self {
335 $( Self::$variant => {
336 paste::paste! {
337 let mut hasher = [<$variant:camel>]::new();
338 hasher.update(input);
339 $crate::alloc::SliceExt::to_vec_in(hasher.finalize().as_slice(), alloc)
340 }
341 }, )*
342 _ => unsafe { unreachable_unchecked() }
344 }
345 }
346 }
347 paste::paste! {
348 $(
349 impl DigestOpExt for [<$variant:camel>] {
350 const OPCODE: DigestOp = DigestOp::$variant;
351
352 #[inline]
353 fn opcode() -> DigestOp {
354 DigestOp::$variant
355 }
356 }
357 )*
358 }
359 };
360}
361
362macro_rules! impl_simple_step {
363 ($variant:ident) => {paste::paste! {
364 impl<A: $crate::alloc::Allocator + Clone> $crate::codec::v1::timestamp::builder::TimestampBuilder<A> {
365 #[doc = concat!("Push the `", stringify!($variant), "` opcode.")]
366 pub fn [< $variant:lower >](&mut self) -> &mut Self {
367 self.push_step(OpCode::[<$variant>])
368 }
369 }
370 }};
371 ($($variant:ident),* $(,)?) => {
372 $(
373 impl_simple_step! { $variant }
374 )*
375 };
376}
377
378macro_rules! impl_step_with_data {
379 ($variant:ident) => {paste::paste! {
380 impl<A: $crate::alloc::Allocator + Clone> $crate::codec::v1::timestamp::builder::TimestampBuilder<A> {
381 #[doc = concat!("Push the `", stringify!($variant), "` opcode.")]
382 pub fn [< $variant:lower >](&mut self, data: $crate::alloc::vec::Vec<u8, A>) -> &mut Self {
383 self.push_immediate_step(OpCode::[<$variant>], data)
384 }
385 }
386 }};
387 ($($variant:ident),* $(,)?) => {
388 $(
389 impl_step_with_data! { $variant }
390 )*
391 };
392}
393
394define_opcodes! {
395 0x02 => SHA1,
396 0x03 => RIPEMD160,
397 0x08 => SHA256,
398 0x67 => KECCAK256,
399 0xf0 => APPEND,
400 0xf1 => PREPEND,
401 0xf2 => REVERSE,
402 0xf3 => HEXLIFY,
403 0x00 => ATTESTATION,
404 0xff => FORK,
405}
406
407define_digest_opcodes! {
408 0x02 => SHA1,
409 0x03 => RIPEMD160,
410 0x08 => SHA256,
411 0x67 => KECCAK256,
412}
413
414impl_simple_step! {
415 SHA1,
416 RIPEMD160,
417 SHA256,
418 KECCAK256,
419 REVERSE,
420 HEXLIFY,
421}
422
423impl_step_with_data! {
424 APPEND,
425 PREPEND,
426}
427
428#[cfg(test)]
429mod tests {
430 use super::*;
431
432 #[test]
433 fn digest_len() {
434 assert_eq!(DigestOp::SHA1.output_size(), 20);
435 assert_eq!(DigestOp::RIPEMD160.output_size(), 20);
436 assert_eq!(DigestOp::SHA256.output_size(), 32);
437 assert_eq!(DigestOp::KECCAK256.output_size(), 32);
438 }
439
440 #[cfg(feature = "serde")]
441 #[test]
442 fn serde_opcode() {
443 let opcode = OpCode::SHA256;
444 let serialized = serde_json::to_string(&opcode).unwrap();
445 assert_eq!(serialized, "\"SHA256\"");
446 let deserialized: OpCode = serde_json::from_str(&serialized).unwrap();
447 assert_eq!(deserialized, opcode);
448
449 let digest_op = DigestOp::SHA256;
450 let serialized = serde_json::to_string(&digest_op).unwrap();
451 assert_eq!(serialized, "\"SHA256\"");
452 let deserialized: DigestOp = serde_json::from_str(&serialized).unwrap();
453 assert_eq!(deserialized, digest_op);
454 }
455}