use alloc::{vec, vec::Vec};
use miden_core::{
Felt, Word, ZERO,
deferred::{DeferredError, Node},
events::EventName,
};
use miden_precompiles::{Limbs, UintDomain, UintPrecompile};
use miden_processor::{
ProcessorState,
advice::{AdviceMutation, AdviceStack},
event::EventError,
};
pub const UINT_FIELD_INV_EVENT_NAME: EventName =
EventName::new("miden::precompiles::fields::field_inv");
pub fn handle_uint_field_inv(
process: &ProcessorState<'_>,
) -> Result<Vec<AdviceMutation>, EventError> {
let input_digest = process.get_stack_word(1);
let (_, canonical_node) = process.require_canonical_deferred_node(input_digest)?;
let tag = canonical_node.tag();
let [op_id, bound_ptr, reserved] = tag.args();
if op_id.as_canonical_u64() != UintPrecompile::VALUE_OP_ID || reserved != ZERO {
return Err(UintFieldInvError::ExpectedUintValue.into());
}
if tag.id() != UintPrecompile::id() {
return Err(UintFieldInvError::ExpectedUintPrecompile.into());
}
let bound_ptr = u32::try_from(bound_ptr.as_canonical_u64())
.map_err(|_| UintFieldInvError::UnknownDomain)?;
let domain = UintDomain::from_bound_ptr(bound_ptr).ok_or(UintFieldInvError::UnknownDomain)?;
if !domain.is_prime_field() {
return Err(UintFieldInvError::UnsupportedDomain.into());
}
let value = limbs_from_value_node(canonical_node, domain)
.map_err(|_| UintFieldInvError::ExpectedUintValue)?;
let inverse = domain.inv(value).ok_or(UintFieldInvError::ZeroValue)?;
let inverse = inverse.map(Felt::from_u32);
let mut advice_stack = AdviceStack::new();
advice_stack.append_dword([
Word::new([inverse[0], inverse[1], inverse[2], inverse[3]]),
Word::new([inverse[4], inverse[5], inverse[6], inverse[7]]),
]);
Ok(vec![AdviceMutation::extend_advice_stack(advice_stack)])
}
fn limbs_from_value_node(node: &Node, domain: UintDomain) -> Result<Limbs, DeferredError> {
let payload = node.payload_for_tag(UintPrecompile::value_tag(domain))?;
let limbs = decode_limbs(payload.as_value()?)?;
if !domain.is_canonical(&limbs) {
return Err(DeferredError::InvalidPayload);
}
Ok(limbs)
}
fn decode_limbs(felts: &[Felt; 8]) -> Result<Limbs, DeferredError> {
let mut limbs = [0u32; 8];
for (i, felt) in felts.iter().enumerate() {
let value = felt.as_canonical_u64();
if value > u32::MAX as u64 {
return Err(DeferredError::InvalidPayload);
}
limbs[i] = value as u32;
}
Ok(limbs)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
enum UintFieldInvError {
#[error("expected uint precompile tag")]
ExpectedUintPrecompile,
#[error("expected a canonical uint VALUE node")]
ExpectedUintValue,
#[error("unknown uint domain")]
UnknownDomain,
#[error("uint domain is not a declared prime field")]
UnsupportedDomain,
#[error("cannot invert zero in a finite field")]
ZeroValue,
}