sim_lib_bridge/
warrant.rs1use 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
12pub 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}