#[cfg(test)]
use crate::error::ExecutorError;
use crate::error::ExecutorResult;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct GlobalId(pub usize);
impl GlobalId {
#[inline]
pub fn as_usize(self) -> usize {
self.0
}
}
impl From<usize> for GlobalId {
#[inline]
fn from(value: usize) -> Self {
Self(value)
}
}
impl From<GlobalId> for usize {
#[inline]
fn from(value: GlobalId) -> Self {
value.0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct InstanceInfo {
pub airgroup_id: usize,
pub air_id: usize,
}
impl InstanceInfo {
#[inline]
pub fn new(airgroup_id: usize, air_id: usize) -> Self {
Self { airgroup_id, air_id }
}
}
impl From<(usize, usize)> for InstanceInfo {
#[inline]
fn from(value: (usize, usize)) -> Self {
Self::new(value.0, value.1)
}
}
impl From<InstanceInfo> for (usize, usize) {
#[inline]
fn from(info: InstanceInfo) -> Self {
(info.airgroup_id, info.air_id)
}
}
pub trait Dctx {
fn instance_info(&self, gid: GlobalId) -> ExecutorResult<InstanceInfo>;
fn is_my_process_instance(&self, gid: GlobalId) -> ExecutorResult<bool>;
fn set_witness_ready(&self, gid: GlobalId, ready: bool);
fn is_first_process(&self) -> bool;
}
pub trait ProofRegistry: Dctx {
fn add_instance(&self, info: InstanceInfo) -> ExecutorResult<GlobalId>;
fn add_instance_assign(&self, info: InstanceInfo) -> ExecutorResult<GlobalId>;
fn add_table(&self, info: InstanceInfo) -> ExecutorResult<GlobalId>;
fn find_instance_id(&self, info: InstanceInfo) -> ExecutorResult<GlobalId>;
fn set_chunks(&self, gid: GlobalId, chunks: &[usize], is_memory_related: bool);
fn write_pub_outs(&self, pub_outs: &[(u64, u32)]);
fn instance_counts(&self) -> std::collections::HashMap<(usize, usize), usize> {
std::collections::HashMap::new()
}
}
#[cfg(test)]
pub(crate) mod fakes {
use super::*;
use std::cell::RefCell;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AddKind {
Instance,
InstanceAssign,
Table,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AddCall {
pub kind: AddKind,
pub info: InstanceInfo,
pub gid: GlobalId,
}
pub struct FakeProofRegistry {
next_id: RefCell<usize>,
pub additions: RefCell<Vec<AddCall>>,
pub witness_ready: RefCell<HashMap<GlobalId, bool>>,
pub set_chunks_calls: RefCell<Vec<(GlobalId, Vec<usize>, bool)>>,
pub pub_outs: RefCell<Vec<(u64, u32)>>,
pub ownership: RefCell<HashMap<GlobalId, bool>>,
}
impl Default for FakeProofRegistry {
fn default() -> Self {
Self {
next_id: RefCell::new(0),
additions: RefCell::default(),
witness_ready: RefCell::default(),
set_chunks_calls: RefCell::default(),
pub_outs: RefCell::default(),
ownership: RefCell::default(),
}
}
}
impl FakeProofRegistry {
pub fn new() -> Self {
Self::default()
}
fn next_gid(&self, kind: AddKind, info: InstanceInfo) -> GlobalId {
let mut next = self.next_id.borrow_mut();
let gid = GlobalId(*next);
*next += 1;
self.additions.borrow_mut().push(AddCall { kind, info, gid });
gid
}
}
impl Dctx for FakeProofRegistry {
fn instance_info(&self, gid: GlobalId) -> ExecutorResult<InstanceInfo> {
self.additions
.borrow()
.iter()
.find(|a| a.gid == gid)
.map(|a| a.info)
.ok_or(ExecutorError::InstanceNotFound { global_id: gid.0 })
}
fn is_my_process_instance(&self, gid: GlobalId) -> ExecutorResult<bool> {
Ok(self.ownership.borrow().get(&gid).copied().unwrap_or(true))
}
fn set_witness_ready(&self, gid: GlobalId, ready: bool) {
self.witness_ready.borrow_mut().insert(gid, ready);
}
fn is_first_process(&self) -> bool {
true
}
}
impl ProofRegistry for FakeProofRegistry {
fn add_instance(&self, info: InstanceInfo) -> ExecutorResult<GlobalId> {
Ok(self.next_gid(AddKind::Instance, info))
}
fn add_instance_assign(&self, info: InstanceInfo) -> ExecutorResult<GlobalId> {
Ok(self.next_gid(AddKind::InstanceAssign, info))
}
fn add_table(&self, info: InstanceInfo) -> ExecutorResult<GlobalId> {
Ok(self.next_gid(AddKind::Table, info))
}
fn find_instance_id(&self, info: InstanceInfo) -> ExecutorResult<GlobalId> {
self.additions
.borrow()
.iter()
.find(|a| a.info == info)
.map(|a| a.gid)
.ok_or(ExecutorError::SecnPlanMissing { phase: "find_instance_id" })
}
fn set_chunks(&self, gid: GlobalId, chunks: &[usize], is_memory_related: bool) {
self.set_chunks_calls.borrow_mut().push((gid, chunks.to_vec(), is_memory_related));
}
fn write_pub_outs(&self, pub_outs: &[(u64, u32)]) {
self.pub_outs.borrow_mut().extend_from_slice(pub_outs);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use fakes::{AddKind, FakeProofRegistry};
#[test]
fn global_id_round_trips_through_usize() {
let gid: GlobalId = 42usize.into();
let back: usize = gid.into();
assert_eq!(back, 42);
assert_eq!(gid.as_usize(), 42);
}
#[test]
fn instance_info_round_trips_through_tuple() {
let info: InstanceInfo = (3, 7).into();
let back: (usize, usize) = info.into();
assert_eq!(back, (3, 7));
assert_eq!(info.airgroup_id, 3);
assert_eq!(info.air_id, 7);
}
#[test]
fn fake_registry_records_add_instance_calls_in_order() {
let reg = FakeProofRegistry::new();
let a = reg.add_instance(InstanceInfo::new(0, 10)).unwrap();
let b = reg.add_instance_assign(InstanceInfo::new(0, 11)).unwrap();
let c = reg.add_table(InstanceInfo::new(0, 12)).unwrap();
let additions = reg.additions.borrow();
assert_eq!(additions.len(), 3);
assert_eq!(additions[0].kind, AddKind::Instance);
assert_eq!(additions[1].kind, AddKind::InstanceAssign);
assert_eq!(additions[2].kind, AddKind::Table);
assert_eq!(additions[0].gid, a);
assert_eq!(additions[1].gid, b);
assert_eq!(additions[2].gid, c);
}
#[test]
fn fake_registry_instance_info_round_trips_assignment() {
let reg = FakeProofRegistry::new();
let info = InstanceInfo::new(1, 99);
let gid = reg.add_instance(info).unwrap();
assert_eq!(reg.instance_info(gid).unwrap(), info);
}
#[test]
fn fake_registry_find_by_info() {
let reg = FakeProofRegistry::new();
let info = InstanceInfo::new(1, 99);
let gid = reg.add_instance(info).unwrap();
assert_eq!(reg.find_instance_id(info).unwrap(), gid);
assert!(reg.find_instance_id(InstanceInfo::new(0, 0)).is_err());
}
#[test]
fn fake_registry_ownership_default_is_owned() {
let reg = FakeProofRegistry::new();
let gid = reg.add_instance(InstanceInfo::new(0, 1)).unwrap();
assert!(reg.is_my_process_instance(gid).unwrap());
}
#[test]
fn fake_registry_ownership_override() {
let reg = FakeProofRegistry::new();
let gid = reg.add_instance(InstanceInfo::new(0, 1)).unwrap();
reg.ownership.borrow_mut().insert(gid, false);
assert!(!reg.is_my_process_instance(gid).unwrap());
}
#[test]
fn fake_registry_records_set_chunks_in_order() {
let reg = FakeProofRegistry::new();
let a = reg.add_instance(InstanceInfo::new(0, 1)).unwrap();
let b = reg.add_instance(InstanceInfo::new(0, 2)).unwrap();
reg.set_chunks(a, &[0, 1, 2], false);
reg.set_chunks(b, &[5], true);
let calls = reg.set_chunks_calls.borrow();
assert_eq!(calls.len(), 2);
assert_eq!(calls[0], (a, vec![0, 1, 2], false));
assert_eq!(calls[1], (b, vec![5], true));
}
#[test]
fn fake_registry_accumulates_pub_outs() {
let reg = FakeProofRegistry::new();
reg.write_pub_outs(&[(0, 0xAA), (1, 0xBB)]);
reg.write_pub_outs(&[(2, 0xCC)]);
assert_eq!(*reg.pub_outs.borrow(), vec![(0, 0xAA), (1, 0xBB), (2, 0xCC)]);
}
}