extern crate alloc;
use alloc::vec::Vec;
use crate::data::{Digest, Reduction, Order};
use crate::call::CallProvider;
use crate::trace::{Tracer, TraceRow};
use crate::reduce::Outcome;
pub type DigestKey = [u64; 4];
#[inline]
pub fn digest_key(d: &Digest) -> DigestKey {
[d[0].as_u64(), d[1].as_u64(), d[2].as_u64(), d[3].as_u64()]
}
pub type JetFn<const N: usize> = fn(
&mut Reduction<N>,
Order, Order, u64, &dyn CallProvider<N>,
&mut dyn Tracer,
u64, &mut TraceRow,
) -> Outcome;
pub type TemplatePredicate<const N: usize> = fn(&Reduction<N>, Order) -> bool;
pub struct JetRegistry<const N: usize> {
exact: Vec<(DigestKey, JetFn<N>)>,
templates: Vec<(TemplatePredicate<N>, JetFn<N>)>,
}
impl<const N: usize> JetRegistry<N> {
pub fn empty() -> Self {
Self { exact: Vec::new(), templates: Vec::new() }
}
pub fn insert_exact(&mut self, key: DigestKey, f: JetFn<N>) {
let pos = self.exact.partition_point(|(k, _)| k < &key);
self.exact.insert(pos, (key, f));
}
pub fn insert_template(&mut self, pred: TemplatePredicate<N>, f: JetFn<N>) {
self.templates.push((pred, f));
}
#[inline]
pub fn lookup_exact(&self, key: &DigestKey) -> Option<JetFn<N>> {
self.exact.binary_search_by_key(key, |(k, _)| *k)
.ok()
.map(|i| self.exact[i].1)
}
pub fn lookup_template(&self, reduction: &Reduction<N>, formula: Order) -> Option<JetFn<N>> {
self.templates.iter()
.find(|(pred, _)| pred(reduction, formula))
.map(|(_, f)| *f)
}
pub fn genesis() -> Self {
super::backends::select()
}
pub fn exact_count(&self) -> usize { self.exact.len() }
pub fn template_count(&self) -> usize { self.templates.len() }
}
pub struct GenesisDigests {
pub poly_eval: DigestKey,
pub merkle_verify: DigestKey,
pub fri_fold: DigestKey,
pub ntt: DigestKey,
pub cyberlink: DigestKey,
pub decider: DigestKey,
}
pub fn compute_genesis_digests() -> GenesisDigests {
use crate::jets::formulas;
let mut ar = Reduction::<4096>::new();
let poly_eval = formulas::build_poly_eval_formula(&mut ar)
.map(|id| digest_key(ar.digest(id).unwrap()))
.unwrap_or([0; 4]);
let merkle_verify = formulas::build_merkle_verify_formula(&mut ar)
.map(|id| digest_key(ar.digest(id).unwrap()))
.unwrap_or([0; 4]);
let fri_fold = formulas::build_fri_fold_formula(&mut ar)
.map(|id| digest_key(ar.digest(id).unwrap()))
.unwrap_or([0; 4]);
let ntt = formulas::build_ntt_formula(&mut ar)
.map(|id| digest_key(ar.digest(id).unwrap()))
.unwrap_or([0; 4]);
let cyberlink = formulas::build_cyberlink_formula(&mut ar)
.map(|id| digest_key(ar.digest(id).unwrap()))
.unwrap_or([0; 4]);
let decider = formulas::build_decider_formula(&mut ar)
.map(|id| digest_key(ar.digest(id).unwrap()))
.unwrap_or([0; 4]);
GenesisDigests { poly_eval, merkle_verify, fri_fold, ntt, cyberlink, decider }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::data::Reduction;
#[test]
fn empty_registry_returns_none() {
let reg = JetRegistry::<1024>::empty();
assert!(reg.lookup_exact(&[0u64; 4]).is_none());
}
#[test]
fn insert_and_lookup_exact() {
let mut reg = JetRegistry::<1024>::empty();
fn dummy_jet<const N: usize>(
_: &mut Reduction<N>, _: Order, _: Order, b: u64,
_: &dyn CallProvider<N>, _: &mut dyn Tracer, _: u64,
_: &mut TraceRow,
) -> Outcome {
Outcome::Halt(b)
}
let key = [1u64, 2, 3, 4];
reg.insert_exact(key, dummy_jet);
assert!(reg.lookup_exact(&key).is_some());
assert!(reg.lookup_exact(&[0u64; 4]).is_none());
}
#[test]
fn sorted_insert_maintains_binary_search() {
let mut reg = JetRegistry::<1024>::empty();
fn jet_a<const N: usize>(
_: &mut Reduction<N>, _: Order, _: Order, b: u64,
_: &dyn CallProvider<N>, _: &mut dyn Tracer, _: u64, _: &mut TraceRow,
) -> Outcome { Outcome::Halt(b) }
fn jet_b<const N: usize>(
_: &mut Reduction<N>, _: Order, _: Order, b: u64,
_: &dyn CallProvider<N>, _: &mut dyn Tracer, _: u64, _: &mut TraceRow,
) -> Outcome { Outcome::Halt(b + 1) }
reg.insert_exact([5, 0, 0, 0], jet_b);
reg.insert_exact([2, 0, 0, 0], jet_a);
assert!(reg.lookup_exact(&[2, 0, 0, 0]).is_some());
assert!(reg.lookup_exact(&[5, 0, 0, 0]).is_some());
assert!(reg.lookup_exact(&[3, 0, 0, 0]).is_none());
}
#[test]
fn template_predicate_matches() {
let mut reg = JetRegistry::<1024>::empty();
fn always_match<const N: usize>(_: &Reduction<N>, _: Order) -> bool { true }
fn jet<const N: usize>(
_: &mut Reduction<N>, _: Order, _: Order, b: u64,
_: &dyn CallProvider<N>, _: &mut dyn Tracer, _: u64, _: &mut TraceRow,
) -> Outcome { Outcome::Halt(b) }
reg.insert_template(always_match, jet);
let ar = Reduction::<1024>::new();
assert!(reg.lookup_template(&ar, 0).is_some());
}
#[test]
fn genesis_digests_are_deterministic() {
let d1 = compute_genesis_digests();
let d2 = compute_genesis_digests();
assert_eq!(d1.poly_eval, d2.poly_eval);
assert_eq!(d1.merkle_verify, d2.merkle_verify);
}
}