essential_state_asm/
lib.rs

1//! Assembly for state read operations.
2//!
3//! # Op Table
4#![doc = essential_asm_gen::gen_ops_docs_table!()]
5#![cfg_attr(not(feature = "std"), no_std)]
6#![forbid(unsafe_code)]
7#![warn(missing_docs)]
8
9#[doc(inline)]
10pub use essential_constraint_asm::{FromBytesError, Word};
11#[doc(inline)]
12pub use op::{StateRead as Op, *};
13#[doc(inline)]
14pub use opcode::{InvalidOpcodeError, NotEnoughBytesError, StateRead as Opcode};
15
16/// Typed representation of an operation its associated data.
17mod op {
18    pub use essential_constraint_asm::{
19        Access, Alu, Constraint, Crypto, Pred, Stack, Temporary, ToBytes, ToOpcode,
20        TotalControlFlow, TryFromBytes,
21    };
22    essential_asm_gen::gen_state_read_op_decls!();
23    essential_asm_gen::gen_state_read_op_impls!();
24    /// Provides the operation type bytes iterators.
25    pub mod bytes_iter {
26        pub use essential_constraint_asm::bytes_iter::*;
27        essential_asm_gen::gen_state_read_op_bytes_iter!();
28    }
29
30    /// Short hand names for the operations.
31    pub mod short {
32        use super::{StateRead as Op, *};
33        essential_asm_gen::gen_state_op_consts!();
34    }
35}
36
37/// Typed representation of the opcode, without any associated data.
38pub mod opcode {
39    pub use essential_constraint_asm::opcode::*;
40    essential_asm_gen::gen_state_read_opcode_decls!();
41    essential_asm_gen::gen_state_read_opcode_impls!();
42}
43
44/// Parse operations from the given iterator yielding bytes.
45///
46/// Returns an iterator yielding `Op` results, erroring in the case that an
47/// invalid opcode is encountered or the iterator contains insufficient bytes
48/// for an operation.
49pub fn from_bytes(
50    bytes: impl IntoIterator<Item = u8>,
51) -> impl Iterator<Item = Result<Op, FromBytesError>> {
52    let mut iter = bytes.into_iter();
53    core::iter::from_fn(move || Op::try_from_bytes(&mut iter))
54}
55
56/// Convert the given iterator yielding operations into and iterator yielding
57/// the serialized form in bytes.
58pub fn to_bytes(ops: impl IntoIterator<Item = Op>) -> impl Iterator<Item = u8> {
59    ops.into_iter().flat_map(|op| op.to_bytes())
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn opcode_roundtrip_u8() {
68        for byte in 0..=u8::MAX {
69            if let Ok(opcode) = Opcode::try_from(byte) {
70                println!("{byte:02X}: {opcode:?}");
71                assert_eq!(u8::from(opcode), byte);
72            }
73        }
74    }
75
76    fn roundtrip(ops: Vec<Op>) {
77        assert!(!ops.is_empty());
78        let bytes: Vec<_> = to_bytes(ops.iter().cloned()).collect();
79        assert_eq!(
80            ops,
81            from_bytes(bytes).collect::<Result<Vec<_>, _>>().unwrap()
82        );
83    }
84
85    #[test]
86    fn roundtrip_args_start() {
87        let ops: Vec<Op> = vec![
88            Stack::Push(0x1234567812345678).into(),
89            Stack::Push(0x0F0F0F0F0F0F0F0F).into(),
90            StateMemory::AllocSlots.into(),
91            StateMemory::Length.into(),
92        ];
93        roundtrip(ops);
94    }
95
96    #[test]
97    #[allow(clippy::useless_conversion)]
98    fn roundtrip_args_end() {
99        let ops: Vec<Op> = vec![
100            StateRead::KeyRange.into(),
101            StateRead::KeyRangeExtern.into(),
102            Stack::Push(0x0F0F0F0F0F0F0F0F).into(),
103        ];
104        roundtrip(ops);
105    }
106
107    #[test]
108    fn roundtrip_no_args() {
109        let ops: Vec<Op> = vec![
110            StateMemory::Store.into(),
111            Access::ThisAddress.into(),
112            StateMemory::Load.into(),
113            Access::ThisContractAddress.into(),
114            StateMemory::Length.into(),
115        ];
116        roundtrip(ops);
117    }
118
119    fn expect_invalid_opcode(opcode_byte: u8) {
120        let bytes = vec![opcode_byte];
121        let err = from_bytes(bytes)
122            .collect::<Result<Vec<_>, _>>()
123            .unwrap_err();
124        match err {
125            FromBytesError::InvalidOpcode(InvalidOpcodeError(byte)) => {
126                assert_eq!(byte, opcode_byte)
127            }
128            _ => panic!("unexpected error variant"),
129        }
130    }
131
132    #[test]
133    fn invalid_opcode_0x00() {
134        let opcode_byte = 0x00;
135        expect_invalid_opcode(opcode_byte);
136    }
137
138    #[test]
139    fn invalid_opcode_0xff() {
140        let opcode_byte = 0xFF;
141        expect_invalid_opcode(opcode_byte);
142    }
143}