#![doc = essential_asm_gen::gen_constraint_ops_docs_table!()]
#![cfg_attr(not(feature = "std"), no_std)]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
use core::fmt;
#[doc(inline)]
pub use essential_types::Word;
#[doc(inline)]
pub use op::{Constraint as Op, *};
#[doc(inline)]
pub use opcode::{Constraint as Opcode, InvalidOpcodeError, NotEnoughBytesError};
mod op {
pub trait ToBytes {
type Bytes: IntoIterator<Item = u8>;
fn to_bytes(&self) -> Self::Bytes;
}
pub trait ToOpcode {
type Opcode;
fn to_opcode(&self) -> Self::Opcode;
}
pub trait TryFromBytes: Sized {
type Error: core::fmt::Debug + core::fmt::Display;
fn try_from_bytes(
bytes: &mut impl Iterator<Item = u8>,
) -> Option<Result<Self, Self::Error>>;
}
essential_asm_gen::gen_constraint_op_decls!();
essential_asm_gen::gen_constraint_op_impls!();
pub mod bytes_iter {
essential_asm_gen::gen_constraint_op_bytes_iter!();
}
}
pub mod opcode {
use core::fmt;
pub trait ParseOp {
type Op;
type Error: core::fmt::Debug + core::fmt::Display;
fn parse_op(&self, bytes: &mut impl Iterator<Item = u8>) -> Result<Self::Op, Self::Error>;
}
#[derive(Debug)]
pub struct InvalidOpcodeError(pub u8);
#[derive(Debug)]
pub struct NotEnoughBytesError;
impl fmt::Display for InvalidOpcodeError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Invalid Opcode 0x{:02X}", self.0)
}
}
impl fmt::Display for NotEnoughBytesError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Provided iterator did not yield enough bytes")
}
}
#[cfg(feature = "std")]
impl std::error::Error for InvalidOpcodeError {}
#[cfg(feature = "std")]
impl std::error::Error for NotEnoughBytesError {}
essential_asm_gen::gen_constraint_opcode_decls!();
essential_asm_gen::gen_constraint_opcode_impls!();
}
#[derive(Debug)]
pub enum FromBytesError {
InvalidOpcode(InvalidOpcodeError),
NotEnoughBytes(NotEnoughBytesError),
}
impl fmt::Display for FromBytesError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("failed to parse ops from bytes: ")?;
match self {
Self::InvalidOpcode(err) => err.fmt(f),
Self::NotEnoughBytes(err) => err.fmt(f),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for FromBytesError {}
impl From<InvalidOpcodeError> for FromBytesError {
fn from(err: InvalidOpcodeError) -> Self {
Self::InvalidOpcode(err)
}
}
impl From<NotEnoughBytesError> for FromBytesError {
fn from(err: NotEnoughBytesError) -> Self {
Self::NotEnoughBytes(err)
}
}
pub fn from_bytes(
bytes: impl IntoIterator<Item = u8>,
) -> impl Iterator<Item = Result<Op, FromBytesError>> {
let mut iter = bytes.into_iter();
core::iter::from_fn(move || Op::try_from_bytes(&mut iter))
}
pub fn to_bytes(ops: impl IntoIterator<Item = Op>) -> impl Iterator<Item = u8> {
ops.into_iter().flat_map(|op| op.to_bytes())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn opcode_roundtrip_u8() {
for byte in 0..=u8::MAX {
if let Ok(opcode) = Opcode::try_from(byte) {
println!("{byte:02X}: {opcode:?}");
assert_eq!(u8::from(opcode), byte);
}
}
}
fn roundtrip(ops: Vec<Op>) {
assert!(!ops.is_empty());
let bytes: Vec<_> = to_bytes(ops.iter().cloned()).collect();
assert_eq!(
ops,
from_bytes(bytes).collect::<Result<Vec<_>, _>>().unwrap()
);
}
#[test]
fn roundtrip_args_start() {
let ops: Vec<Op> = vec![
Stack::Push(0x1234567812345678).into(),
Stack::Push(0x0F0F0F0F0F0F0F0F).into(),
Stack::Swap.into(),
Stack::Dup.into(),
];
roundtrip(ops);
}
#[test]
fn roundtrip_args_end() {
let ops: Vec<Op> = vec![
Stack::Swap.into(),
Stack::Dup.into(),
Stack::Push(0x0F0F0F0F0F0F0F0F).into(),
];
roundtrip(ops);
}
#[test]
fn roundtrip_args_interspersed() {
let ops: Vec<Op> = vec![
Stack::Push(0x1234567812345678).into(),
Stack::Swap.into(),
Stack::Push(0x0F0F0F0F0F0F0F0F).into(),
Stack::Dup.into(),
Stack::Push(0x1234567812345678).into(),
];
roundtrip(ops);
}
#[test]
fn roundtrip_no_args() {
let ops: Vec<Op> = vec![
Access::ThisAddress.into(),
Access::ThisContractAddress.into(),
Stack::Swap.into(),
Stack::Dup.into(),
Crypto::Sha256.into(),
];
roundtrip(ops);
}
fn expect_invalid_opcode(opcode_byte: u8) {
let bytes = vec![opcode_byte];
let err = from_bytes(bytes)
.collect::<Result<Vec<_>, _>>()
.unwrap_err();
match err {
FromBytesError::InvalidOpcode(InvalidOpcodeError(byte)) => {
assert_eq!(byte, opcode_byte)
}
_ => panic!("unexpected error variant"),
}
}
#[test]
fn invalid_opcode_0x00() {
let opcode_byte = 0x00;
expect_invalid_opcode(opcode_byte);
}
#[test]
fn invalid_opcode_0xff() {
let opcode_byte = 0xFF;
expect_invalid_opcode(opcode_byte);
}
#[test]
fn not_enough_bytes() {
let opcode_byte = opcode::Stack::Push as u8;
let bytes = vec![opcode_byte];
let err = from_bytes(bytes)
.collect::<Result<Vec<_>, _>>()
.unwrap_err();
match err {
FromBytesError::NotEnoughBytes(_) => (),
_ => panic!("unexpected error variant"),
}
}
}