essential_constraint_asm/
lib.rs1#![doc = essential_asm_gen::gen_constraint_ops_docs_table!()]
5#![cfg_attr(not(feature = "std"), no_std)]
6#![forbid(unsafe_code)]
7#![warn(missing_docs)]
8
9use core::fmt;
10#[doc(inline)]
11pub use essential_types::Word;
12#[doc(inline)]
13pub use op::{Constraint as Op, *};
14#[doc(inline)]
15pub use opcode::{Constraint as Opcode, InvalidOpcodeError, NotEnoughBytesError};
16
17mod op {
19 pub trait ToBytes {
21 type Bytes: IntoIterator<Item = u8>;
23 fn to_bytes(&self) -> Self::Bytes;
25 }
26
27 pub trait ToOpcode {
29 type Opcode;
31 fn to_opcode(&self) -> Self::Opcode;
33 }
34
35 pub trait TryFromBytes: Sized {
37 type Error: core::fmt::Debug + core::fmt::Display;
39 fn try_from_bytes(
43 bytes: &mut impl Iterator<Item = u8>,
44 ) -> Option<Result<Self, Self::Error>>;
45 }
46
47 essential_asm_gen::gen_constraint_op_decls!();
48 essential_asm_gen::gen_constraint_op_impls!();
49
50 pub mod bytes_iter {
52 essential_asm_gen::gen_constraint_op_bytes_iter!();
53 }
54
55 pub mod short {
57 use super::{Constraint as Op, *};
58 essential_asm_gen::gen_constraint_op_consts!();
59 }
60}
61
62pub mod opcode {
64 use core::fmt;
65
66 pub trait ParseOp {
68 type Op;
70 type Error: core::fmt::Debug + core::fmt::Display;
72 fn parse_op(&self, bytes: &mut impl Iterator<Item = u8>) -> Result<Self::Op, Self::Error>;
79 }
80
81 #[derive(Debug)]
83 pub struct InvalidOpcodeError(pub u8);
84
85 #[derive(Debug)]
89 pub struct NotEnoughBytesError;
90
91 impl fmt::Display for InvalidOpcodeError {
92 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93 write!(f, "Invalid Opcode 0x{:02X}", self.0)
94 }
95 }
96
97 impl fmt::Display for NotEnoughBytesError {
98 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
99 write!(f, "Provided iterator did not yield enough bytes")
100 }
101 }
102
103 #[cfg(feature = "std")]
104 impl std::error::Error for InvalidOpcodeError {}
105
106 #[cfg(feature = "std")]
107 impl std::error::Error for NotEnoughBytesError {}
108
109 essential_asm_gen::gen_constraint_opcode_decls!();
110 essential_asm_gen::gen_constraint_opcode_impls!();
111}
112
113#[derive(Debug)]
115pub enum FromBytesError {
116 InvalidOpcode(InvalidOpcodeError),
118 NotEnoughBytes(NotEnoughBytesError),
120}
121
122impl fmt::Display for FromBytesError {
123 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
124 f.write_str("failed to parse ops from bytes: ")?;
125 match self {
126 Self::InvalidOpcode(err) => err.fmt(f),
127 Self::NotEnoughBytes(err) => err.fmt(f),
128 }
129 }
130}
131
132#[cfg(feature = "std")]
133impl std::error::Error for FromBytesError {}
134
135impl From<InvalidOpcodeError> for FromBytesError {
136 fn from(err: InvalidOpcodeError) -> Self {
137 Self::InvalidOpcode(err)
138 }
139}
140
141impl From<NotEnoughBytesError> for FromBytesError {
142 fn from(err: NotEnoughBytesError) -> Self {
143 Self::NotEnoughBytes(err)
144 }
145}
146
147pub fn from_bytes(
153 bytes: impl IntoIterator<Item = u8>,
154) -> impl Iterator<Item = Result<Op, FromBytesError>> {
155 let mut iter = bytes.into_iter();
156 core::iter::from_fn(move || Op::try_from_bytes(&mut iter))
157}
158
159pub fn to_bytes(ops: impl IntoIterator<Item = Op>) -> impl Iterator<Item = u8> {
162 ops.into_iter().flat_map(|op| op.to_bytes())
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn opcode_roundtrip_u8() {
171 for byte in 0..=u8::MAX {
172 if let Ok(opcode) = Opcode::try_from(byte) {
173 println!("{byte:02X}: {opcode:?}");
174 assert_eq!(u8::from(opcode), byte);
175 }
176 }
177 }
178
179 fn roundtrip(ops: Vec<Op>) {
180 assert!(!ops.is_empty());
181 let bytes: Vec<_> = to_bytes(ops.iter().cloned()).collect();
182 assert_eq!(
183 ops,
184 from_bytes(bytes).collect::<Result<Vec<_>, _>>().unwrap()
185 );
186 }
187
188 #[test]
189 fn roundtrip_args_start() {
190 let ops: Vec<Op> = vec![
191 Stack::Push(0x1234567812345678).into(),
192 Stack::Push(0x0F0F0F0F0F0F0F0F).into(),
193 Stack::Swap.into(),
194 Stack::Dup.into(),
195 ];
196 roundtrip(ops);
197 }
198
199 #[test]
200 fn roundtrip_args_end() {
201 let ops: Vec<Op> = vec![
202 Stack::Swap.into(),
203 Stack::Dup.into(),
204 Stack::Push(0x0F0F0F0F0F0F0F0F).into(),
205 ];
206 roundtrip(ops);
207 }
208
209 #[test]
210 fn roundtrip_args_interspersed() {
211 let ops: Vec<Op> = vec![
212 Stack::Push(0x1234567812345678).into(),
213 Stack::Swap.into(),
214 Stack::Push(0x0F0F0F0F0F0F0F0F).into(),
215 Stack::Dup.into(),
216 Stack::Push(0x1234567812345678).into(),
217 ];
218 roundtrip(ops);
219 }
220
221 #[test]
222 fn roundtrip_no_args() {
223 let ops: Vec<Op> = vec![
224 Access::ThisAddress.into(),
225 Access::ThisContractAddress.into(),
226 Stack::Swap.into(),
227 Stack::Dup.into(),
228 Crypto::Sha256.into(),
229 ];
230 roundtrip(ops);
231 }
232
233 fn expect_invalid_opcode(opcode_byte: u8) {
234 let bytes = vec![opcode_byte];
235 let err = from_bytes(bytes)
236 .collect::<Result<Vec<_>, _>>()
237 .unwrap_err();
238 match err {
239 FromBytesError::InvalidOpcode(InvalidOpcodeError(byte)) => {
240 assert_eq!(byte, opcode_byte)
241 }
242 _ => panic!("unexpected error variant"),
243 }
244 }
245
246 #[test]
247 fn invalid_opcode_0x00() {
248 let opcode_byte = 0x00;
249 expect_invalid_opcode(opcode_byte);
250 }
251
252 #[test]
253 fn invalid_opcode_0xff() {
254 let opcode_byte = 0xFF;
255 expect_invalid_opcode(opcode_byte);
256 }
257
258 #[test]
259 fn not_enough_bytes() {
260 let opcode_byte = opcode::Stack::Push as u8;
261 let bytes = vec![opcode_byte];
262 let err = from_bytes(bytes)
263 .collect::<Result<Vec<_>, _>>()
264 .unwrap_err();
265 match err {
266 FromBytesError::NotEnoughBytes(_) => (),
267 _ => panic!("unexpected error variant"),
268 }
269 }
270}