Skip to main content

sim_lib_bridge/
warrant.rs

1use std::collections::{BTreeMap, BTreeSet};
2
3use sim_codec_bridge::{
4    BridgeBook, BridgePacket, BridgeWarrant, BridgeWarrantPolicy, content_id_string,
5    frame_book_content_id, move_book_content_id, part_spec_content_id,
6};
7use sim_kernel::{ContentId, Cx, Result, Symbol};
8
9use crate::materialize::fetch_obligation;
10use crate::report::BridgeObligation;
11
12/// Verifies a packet warrant against the local bridge book.
13///
14/// A warrant miss is a Fetch obligation, never a hard fail or silent accept.
15pub fn verify_warrant(
16    _cx: &mut Cx,
17    book: &BridgeBook,
18    packet: &BridgePacket,
19) -> Result<Vec<BridgeObligation>> {
20    if book.warrant_policy == BridgeWarrantPolicy::SharedTrust {
21        return Ok(Vec::new());
22    }
23    let Some(warrant) = &packet.warrant else {
24        return Ok(vec![fetch_obligation("warrant", "bridge/Warrant")]);
25    };
26
27    let mut obligations = Vec::new();
28    check_book_id(
29        &mut obligations,
30        "warrant/moves",
31        &warrant.moves,
32        &move_book_content_id(&book.moves)?,
33    );
34    check_book_id(
35        &mut obligations,
36        "warrant/frames",
37        &warrant.frames,
38        &frame_book_content_id(&book.frames)?,
39    );
40    check_part_ids(&mut obligations, book, packet, warrant)?;
41    Ok(obligations)
42}
43
44fn check_book_id(
45    obligations: &mut Vec<BridgeObligation>,
46    path: &str,
47    declared: &ContentId,
48    local: &ContentId,
49) {
50    if declared != local {
51        obligations.push(fetch_content_id(path, declared));
52    }
53}
54
55fn check_part_ids(
56    obligations: &mut Vec<BridgeObligation>,
57    book: &BridgeBook,
58    packet: &BridgePacket,
59    warrant: &BridgeWarrant,
60) -> Result<()> {
61    let declared = warrant
62        .parts
63        .iter()
64        .map(|(kind, cid)| (kind.clone(), cid))
65        .collect::<BTreeMap<Symbol, &ContentId>>();
66    let mut seen = BTreeSet::new();
67    for (kind, declared_cid) in &warrant.parts {
68        let path = part_path(kind);
69        if !seen.insert(kind.clone()) {
70            obligations.push(fetch_content_id(path, declared_cid));
71        }
72    }
73
74    let mut used = BTreeSet::new();
75    for part in &packet.body {
76        if !used.insert(part.kind.clone()) {
77            continue;
78        }
79        let path = part_path(&part.kind);
80        let Some(declared_cid) = declared.get(&part.kind).copied() else {
81            obligations.push(fetch_missing_part(&path, &part.kind));
82            continue;
83        };
84        let Some(spec) = book.parts.spec(&part.kind) else {
85            obligations.push(fetch_content_id(path, declared_cid));
86            continue;
87        };
88        let local = part_spec_content_id(spec)?;
89        if declared_cid != &local {
90            obligations.push(fetch_content_id(path, declared_cid));
91        }
92    }
93
94    for (kind, declared_cid) in &warrant.parts {
95        if !used.contains(kind) {
96            obligations.push(fetch_content_id(part_path(kind), declared_cid));
97        }
98    }
99    Ok(())
100}
101
102fn fetch_missing_part(path: &str, kind: &Symbol) -> BridgeObligation {
103    fetch_obligation(
104        path.to_owned(),
105        format!("bridge/Fetch part spec {}", kind.as_qualified_str()),
106    )
107}
108
109fn fetch_content_id(path: impl Into<String>, id: &ContentId) -> BridgeObligation {
110    fetch_obligation(path, format!("bridge/Fetch {}", content_id_string(id)))
111}
112
113fn part_path(kind: &Symbol) -> String {
114    format!("warrant/parts/{}", kind.as_qualified_str())
115}