use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, vec::Vec};
use core::error::Error;
use miden_crypto::{Felt, Word, ZERO, hash::poseidon2::Poseidon2};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{
events::{EventId, EventName},
serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
};
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(
all(feature = "arbitrary", test),
miden_test_serde_macros::serde_test(binary_serde(true))
)]
pub struct PrecompileRequest {
event_id: EventId,
calldata: Vec<u8>,
}
impl PrecompileRequest {
pub fn new(event_id: EventId, calldata: Vec<u8>) -> Self {
Self { event_id, calldata }
}
pub fn calldata(&self) -> &[u8] {
&self.calldata
}
pub fn event_id(&self) -> EventId {
self.event_id
}
}
impl Serializable for PrecompileRequest {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.event_id.write_into(target);
self.calldata.write_into(target);
}
}
impl Deserializable for PrecompileRequest {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let event_id = EventId::read_from(source)?;
let calldata = Vec::<u8>::read_from(source)?;
Ok(Self { event_id, calldata })
}
}
pub type PrecompileTranscriptState = Word;
pub type PrecompileTranscriptDigest = Word;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PrecompileCommitment {
tag: Word,
comm: Word,
}
impl PrecompileCommitment {
pub fn new(tag: Word, comm: Word) -> Self {
Self { tag, comm }
}
pub fn tag(&self) -> Word {
self.tag
}
pub fn comm_calldata(&self) -> Word {
self.comm
}
pub fn to_elements(&self) -> [Felt; 8] {
let words = [self.tag, self.comm];
Word::words_as_elements(&words).try_into().unwrap()
}
pub fn event_id(&self) -> EventId {
EventId::from_felt(self.tag[0])
}
}
#[derive(Default, Clone)]
pub struct PrecompileVerifierRegistry {
verifiers: BTreeMap<EventId, (EventName, Arc<dyn PrecompileVerifier>)>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
events::EventId,
serde::{BudgetedReader, ByteWriter, DeserializationError, SliceReader},
};
#[test]
fn precompile_request_rejects_over_budget_calldata_len() {
let mut bytes = Vec::new();
EventId::from_u64(0).write_into(&mut bytes);
bytes.write_usize(2);
let budget = bytes.len() + 1;
let mut reader = BudgetedReader::new(SliceReader::new(&bytes), budget);
let err = PrecompileRequest::read_from(&mut reader).unwrap_err();
let DeserializationError::InvalidValue(message) = err else {
panic!("expected InvalidValue error");
};
assert!(message.contains("requested 2 elements"));
}
}
impl PrecompileVerifierRegistry {
pub fn new() -> Self {
Self { verifiers: BTreeMap::new() }
}
pub fn with_verifier(
mut self,
event_name: &EventName,
verifier: Arc<dyn PrecompileVerifier>,
) -> Self {
let event_id = event_name.to_event_id();
self.verifiers.insert(event_id, (event_name.clone(), verifier));
self
}
pub fn merge(&mut self, other: &Self) {
for (event_id, (event_name, verifier)) in other.verifiers.iter() {
self.verifiers.insert(*event_id, (event_name.clone(), verifier.clone()));
}
}
pub fn requests_transcript(
&self,
requests: &[PrecompileRequest],
) -> Result<PrecompileTranscript, PrecompileVerificationError> {
let mut transcript = PrecompileTranscript::new();
for (index, PrecompileRequest { event_id, calldata }) in requests.iter().enumerate() {
let (event_name, verifier) = self.verifiers.get(event_id).ok_or(
PrecompileVerificationError::VerifierNotFound { index, event_id: *event_id },
)?;
let precompile_commitment = verifier.verify(calldata).map_err(|error| {
PrecompileVerificationError::PrecompileError {
index,
event_name: event_name.clone(),
error,
}
})?;
transcript.record(precompile_commitment);
}
Ok(transcript)
}
}
pub trait PrecompileVerifier: Send + Sync {
fn verify(&self, calldata: &[u8]) -> Result<PrecompileCommitment, PrecompileError>;
}
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub struct PrecompileTranscript {
state: Word,
}
impl PrecompileTranscript {
pub fn new() -> Self {
Self::default()
}
pub fn from_state(state: PrecompileTranscriptState) -> Self {
Self { state }
}
pub fn state(&self) -> PrecompileTranscriptState {
self.state
}
pub fn record(&mut self, commitment: PrecompileCommitment) {
let mut state = [ZERO; Poseidon2::STATE_WIDTH];
let comm = commitment.comm_calldata();
let tag = commitment.tag();
state[Poseidon2::RATE0_RANGE].copy_from_slice(comm.as_elements());
state[Poseidon2::RATE1_RANGE].copy_from_slice(tag.as_elements());
state[Poseidon2::CAPACITY_RANGE].copy_from_slice(self.state.as_elements());
Poseidon2::apply_permutation(&mut state);
self.state = Word::new(state[Poseidon2::CAPACITY_RANGE].try_into().unwrap());
}
pub fn finalize(self) -> PrecompileTranscriptDigest {
let mut state = [ZERO; Poseidon2::STATE_WIDTH];
state[Poseidon2::CAPACITY_RANGE].copy_from_slice(self.state.as_elements());
Poseidon2::apply_permutation(&mut state);
PrecompileTranscriptDigest::new(state[Poseidon2::DIGEST_RANGE].try_into().unwrap())
}
}
pub type PrecompileError = Box<dyn Error + Send + Sync + 'static>;
#[derive(Debug, thiserror::Error)]
pub enum PrecompileVerificationError {
#[error("no verifier found for request #{index} for event with ID: {event_id}")]
VerifierNotFound { index: usize, event_id: EventId },
#[error("verification error for request #{index} for event '{event_name}'")]
PrecompileError {
index: usize,
event_name: EventName,
#[source]
error: PrecompileError,
},
}
#[cfg(all(feature = "arbitrary", test))]
impl proptest::prelude::Arbitrary for PrecompileRequest {
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
use proptest::prelude::*;
(any::<EventId>(), proptest::collection::vec(any::<u8>(), 0..=1000))
.prop_map(|(event_id, calldata)| PrecompileRequest::new(event_id, calldata))
.boxed()
}
type Strategy = proptest::prelude::BoxedStrategy<Self>;
}