use super::*;
pub(super) fn check_verdict_record(
record: &Record,
prior: &Prior<'_>,
rules: &VerifierRules,
) -> Option<ReasonCode> {
match record.compute_id() {
Ok(id) if id == record.id => {}
_ => return Some(ReasonCode::InvalidPayload),
}
if record.schema != schema_id(SCHEMA_VERDICT) {
return Some(ReasonCode::KindSchemaMismatch);
}
if record.author.type_ != AuthorType::Verifier {
return Some(ReasonCode::InvalidPayload);
}
if record.author.signature.is_some() {
return Some(ReasonCode::InvalidPayload);
}
if record.space != rules.space {
return Some(ReasonCode::InvalidPayload);
}
if record.evidence != Evidence::Deterministic {
return Some(ReasonCode::InvalidPayload);
}
if !decodes_canonically::<VerdictData>(&record.data) {
return Some(ReasonCode::InvalidPayload);
}
let [subject_ref] = record.refs.as_slice() else {
return Some(ReasonCode::InvalidPayload);
};
if subject_ref.type_ != RefType::Cause {
return Some(ReasonCode::InvalidPayload);
}
let subject_id = subject_ref.target;
let Some(subject) = prior.find(subject_id) else {
return Some(ReasonCode::RefUnresolved);
};
if subject.kind == Kind::Verdict {
return Some(ReasonCode::InvalidPayload);
}
if subject.space != record.space || subject.thread != record.thread {
return Some(ReasonCode::InvalidPayload);
}
if prior.has_verdict_for(subject_id) {
return Some(ReasonCode::InvalidPayload);
}
None
}
pub(super) struct Prior<'a> {
records: &'a [Record],
by_id: &'a HashMap<RecordId, usize>,
verdicted_subjects: &'a HashSet<RecordId>,
}
impl<'a> Prior<'a> {
pub(super) fn find(&self, id: RecordId) -> Option<&'a Record> {
self.by_id.get(&id).map(|&pos| &self.records[pos])
}
pub(super) fn has_verdict_for(&self, subject: RecordId) -> bool {
self.verdicted_subjects.contains(&subject)
}
}
#[derive(Default)]
pub(super) struct PriorIndex {
by_id: HashMap<RecordId, usize>,
verdicted_subjects: HashSet<RecordId>,
}
impl PriorIndex {
pub(super) fn of(records: &[Record]) -> Self {
let mut index = Self::default();
for (pos, record) in records.iter().enumerate() {
index.insert(pos, record);
}
index
}
pub(super) fn insert(&mut self, pos: usize, record: &Record) {
self.by_id.entry(record.id).or_insert(pos);
if record.kind == Kind::Verdict {
for r in &record.refs {
if r.type_ == RefType::Cause {
self.verdicted_subjects.insert(r.target);
}
}
}
}
pub(super) fn view<'a>(&'a self, records: &'a [Record]) -> Prior<'a> {
Prior {
records,
by_id: &self.by_id,
verdicted_subjects: &self.verdicted_subjects,
}
}
}