#[cfg(any(test, feature = "std"))]
use alloc::vec;
use alloc::vec::Vec;
#[cfg(any(test, feature = "std"))]
use std::sync::OnceLock;
#[cfg(all(feature = "concurrent", any(test, feature = "std")))]
use miden_ace_codegen::order_from_tag;
#[cfg(all(not(feature = "concurrent"), any(test, feature = "std")))]
use miden_ace_codegen::subtree_leaves;
#[cfg(any(test, feature = "std"))]
use miden_ace_codegen::{
FactoredCircuitFactory, PackedLeafScratch, padding_leaf, path_in_verified_tree, verify_row,
};
#[cfg(any(test, feature = "std"))]
use miden_core::field::QuadFelt;
use miden_core::{Felt, Word};
#[cfg(any(test, feature = "std"))]
use miden_crypto::merkle::{MerklePath, MerkleTree};
#[cfg(all(feature = "concurrent", any(test, feature = "std")))]
use miden_precompiles_air::NUM_CHIPLETS;
#[cfg(test)]
use crate::ace::PVM_ORDER_COUNT;
#[cfg(any(test, feature = "std"))]
use crate::ace::{PVM_REGISTRY_LAYOUT, build_precompile_factored_ace_circuit};
mod data;
pub(crate) use data::PVM_ACE_REGISTRY_LEVEL12_ROW;
pub use data::PVM_ACE_REGISTRY_ROOT;
#[cfg(test)]
pub use data::{PVM_CIRCUIT_SHAPE, PVM_PREPROCESSED_COMMITMENT};
pub use miden_precompiles_air::PVM_RELATION_DIGEST;
pub const PVM_REGISTRY_ROW_DEPTH: usize = 12;
#[cfg(any(test, feature = "registry-tools"))]
pub(crate) const PVM_PROTOCOL_ID: u64 = 1;
#[cfg(any(test, feature = "registry-tools"))]
pub(crate) fn relation_digest_for_root(root: &Word) -> [Felt; 4] {
miden_air::config::relation_digest(PVM_PROTOCOL_ID, root)
}
#[cfg(any(test, feature = "std"))]
const MISMATCH_HINT: &str = "If this protocol change is intentional, run \
`make regenerate-pvm-registry` and record the break; otherwise inspect the unexpected \
registry drift before updating constants";
#[cfg(any(test, feature = "std"))]
pub fn pvm_ace_registry_path(tag: u32) -> Option<(Word, MerklePath)> {
if (tag as usize) >= PVM_REGISTRY_LAYOUT.leaf_count() {
return None;
}
let subtree_index = tag as usize / PVM_REGISTRY_LAYOUT.leaves_per_subtree();
let leaves = cached_leaves_for_subtree(subtree_index);
let subtree = MerkleTree::new(leaves).expect("subtree has power-of-two leaves");
Some(
path_in_verified_tree(
&PVM_REGISTRY_LAYOUT,
verified_pyramid(),
&subtree,
tag,
MISMATCH_HINT,
)
.expect("bounded tag and verified pyramid must produce a registry path"),
)
}
#[cfg(any(test, feature = "std"))]
fn verified_pyramid() -> &'static Vec<Vec<Word>> {
static PYRAMID: OnceLock<Vec<Vec<Word>>> = OnceLock::new();
PYRAMID.get_or_init(|| {
let row: Vec<Word> = PVM_ACE_REGISTRY_LEVEL12_ROW
.iter()
.map(|node| Word::new(node.map(Felt::new_unchecked)))
.collect();
verify_row(&PVM_REGISTRY_LAYOUT, &row, registry_root(), MISMATCH_HINT)
})
}
pub fn registry_root() -> Word {
Word::new(PVM_ACE_REGISTRY_ROOT.map(Felt::new_unchecked))
}
#[cfg(any(test, feature = "std"))]
pub(crate) fn factory() -> &'static FactoredCircuitFactory<QuadFelt> {
static FACTORY: OnceLock<FactoredCircuitFactory<QuadFelt>> = OnceLock::new();
FACTORY.get_or_init(|| {
let factored =
build_precompile_factored_ace_circuit().expect("precompile composition must build");
FactoredCircuitFactory::new(factored).expect("factory construction cross-checks must pass")
})
}
#[cfg(any(test, feature = "std"))]
fn active_subtree_count() -> usize {
PVM_REGISTRY_LAYOUT
.order_count()
.div_ceil(PVM_REGISTRY_LAYOUT.leaves_per_subtree())
}
#[cfg(any(test, feature = "std"))]
fn active_subtree_leaves(subtree_index: usize) -> &'static [Word] {
static CACHE: OnceLock<Vec<OnceLock<Vec<Word>>>> = OnceLock::new();
let cache =
CACHE.get_or_init(|| (0..active_subtree_count()).map(|_| OnceLock::new()).collect());
cache[subtree_index].get_or_init(|| leaves_for_subtree(subtree_index))
}
#[cfg(any(test, feature = "std"))]
fn padding_subtree_leaves() -> &'static [Word] {
static PADDING: OnceLock<Vec<Word>> = OnceLock::new();
PADDING.get_or_init(|| vec![padding_leaf(); PVM_REGISTRY_LAYOUT.leaves_per_subtree()])
}
#[cfg(any(test, feature = "std"))]
fn cached_leaves_for_subtree(subtree_index: usize) -> &'static [Word] {
if subtree_index >= active_subtree_count() {
padding_subtree_leaves()
} else {
active_subtree_leaves(subtree_index)
}
}
#[cfg(all(feature = "concurrent", any(test, feature = "std")))]
const LEAF_CHUNK: usize = 64;
#[cfg(any(test, feature = "std"))]
fn leaves_for_subtree(subtree_index: usize) -> Vec<Word> {
let factory = factory();
#[cfg(feature = "concurrent")]
{
use rayon::prelude::*;
let leaves = PVM_REGISTRY_LAYOUT.leaves_per_subtree();
let start = subtree_index * leaves;
(0..leaves)
.into_par_iter()
.chunks(LEAF_CHUNK)
.map_init(PackedLeafScratch::new, |scratch, offsets| {
subtree_leaf_range(factory, start, &offsets, scratch)
})
.flatten()
.collect()
}
#[cfg(not(feature = "concurrent"))]
{
let mut scratch = PackedLeafScratch::new();
subtree_leaves(factory, &PVM_REGISTRY_LAYOUT, subtree_index, &mut scratch)
.expect("registry leaves must encode for every proof order")
}
}
#[cfg(all(feature = "concurrent", any(test, feature = "std")))]
fn subtree_leaf_range(
factory: &FactoredCircuitFactory<QuadFelt>,
start: usize,
offsets: &[usize],
scratch: &mut PackedLeafScratch,
) -> Vec<Word> {
let orders: Vec<Vec<usize>> = offsets
.iter()
.filter_map(|&offset| order_from_tag((start + offset) as u32, NUM_CHIPLETS))
.collect();
let order_refs: Vec<&[usize]> = orders.iter().map(Vec::as_slice).collect();
let mut leaves = Vec::with_capacity(offsets.len());
if !order_refs.is_empty() {
factory
.leaves_for_orders(&order_refs, scratch, &mut leaves)
.expect("registry leaves must encode for every proof order");
}
leaves.resize(offsets.len(), padding_leaf());
leaves
}
#[cfg(test)]
mod tests;