warriors/joy/rs/state_native.rs

use super::*;
use nebu::Goldilocks as F;
use nox::{CallProvider, LookProvider, Order, Outcome, Reduction, VecTrace};
struct Calls<'a> {
    state: &'a StateCertificate,
    secrets: &'a [u64],
    next: std::sync::atomic::AtomicUsize,
}
impl LookProvider for Calls<'_> {
    fn look(&self, _root: F, ns: F, key: F) -> Option<F> {
        self.state.cell(ns.as_u64(), key.as_u64()).map(F::new)
    }
}
impl<const N: usize> CallProvider<N> for Calls<'_> {
    fn provide(&self, arena: &mut Reduction<N>, _: F, _: Order) -> Option<Order> {
        let i = self.next.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
        arena.atom(F::new(*self.secrets.get(i)?))
    }
}
impl Warrior {
    pub fn run_state_certificate(
        &self,
        bundle: &ProgramBundle,
        input: &ProgramInput,
        certificate: &StateCertificate,
        budget: u64,
    ) -> Result<ExecutionResult, String> {
        if bundle.target_vm != "nox"
            || !input.digests.is_empty()
            || input
                .public
                .iter()
                .chain(&input.secret)
                .any(|&v| v >= nebu::field::P)
        {
            return Err("invalid state execution inputs".into());
        }
        let root = certificate.root()?;
        certificate.verify(root)?;
        std::thread::scope(|scope| {
            std::thread::Builder::new()
                .name("joy-state-reduce".into())
                .stack_size(256 * 1024 * 1024)
                .spawn_scoped(scope, move || {
                    let mut arena = Reduction::<{ 1 << 18 }>::new();
                    let formula = crate::formula::parse(&mut arena, bundle.assembly.trim())?;
                    let mut object = crate::formula::build_subject(&mut arena, &input.public)?;
                    if bundle.reads_state {
                        let mut tail = arena
                            .atom(F::new(root[3]))
                            .ok_or("state root allocation failed")?;
                        for &limb in root[..3].iter().rev() {
                            let head = arena
                                .atom(F::new(limb))
                                .ok_or("state root allocation failed")?;
                            tail = arena
                                .pair(head, tail)
                                .ok_or("state root allocation failed")?;
                        }
                        object = arena
                            .pair(tail, object)
                            .ok_or("state subject allocation failed")?;
                    }
                    let mut trace = VecTrace::default();
                    let calls = Calls {
                        state: certificate,
                        secrets: &input.secret,
                        next: std::sync::atomic::AtomicUsize::new(0),
                    };
                    let result = match nox::reduce(
                        &mut arena, object, formula, budget, &calls, &mut trace,
                    ) {
                        Outcome::Ok(result, _) => result,
                        _ => return Err("native public state execution failed".into()),
                    };
                    for row in &trace.0 {
                        if row.r()[0] == 17
                            && [row.r()[4], row.r()[11], row.r()[12], row.r()[13]] != root
                        {
                            return Err("native lookup used another state root".into());
                        }
                    }
                    Ok(ExecutionResult {
                        output: crate::formula::leaves(&arena, result)?,
                        cycle_count: trace.0.len() as u64,
                    })
                })
                .map_err(|e| e.to_string())?
                .join()
                .map_err(|_| "state execution worker failed".to_string())?
        })
    }
}

Graph