1use super::cap::{Cap, CapHash};
39
40pub fn cap_hash(cap: &Cap) -> CapHash {
43 ssz::hash_tree_root(cap)
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49
50 use crate::cap::{CapHashOrRef, TypeCap};
51 use crate::cnode::CNodeCap;
52 use crate::data::{DataCap, DataContent};
53 use crate::image_cap::ImageCap;
54 use crate::instance::InstanceCap;
55 use crate::page::{PageBytes, PageRef, PageSlot};
56 use crate::slot::SlotIdx;
57
58 #[test]
59 fn type_cap_hash_deterministic() {
60 let chain = [0xAA; 32];
61 let a: Cap = Cap::Type(TypeCap {
62 image_hash_chain: chain,
63 });
64 let b: Cap = Cap::Type(TypeCap {
65 image_hash_chain: chain,
66 });
67 assert_eq!(cap_hash(&a), cap_hash(&b));
68 let c: Cap = Cap::Type(TypeCap {
70 image_hash_chain: [0xBB; 32],
71 });
72 assert_ne!(cap_hash(&a), cap_hash(&c));
73 }
74
75 #[test]
76 fn cap_variants_have_distinct_hashes() {
77 let t: Cap = Cap::Type(TypeCap {
81 image_hash_chain: [0; 32],
82 });
83 let cn: Cap = Cap::CNode(CNodeCap::new(0).unwrap());
84 assert_ne!(cap_hash(&t), cap_hash(&cn));
85 }
86
87 #[test]
88 fn data_inline_hash_includes_size() {
89 let bytes_a: Vec<u8> = b"abc".to_vec();
90 let bytes_b: Vec<u8> = b"abc".to_vec();
91 let mut bytes_a_padded: Vec<u8> = vec![0u8; crate::data::PAGE_SIZE];
95 bytes_a_padded[..bytes_a.len()].copy_from_slice(bytes_a.as_slice());
96 let mut bytes_b_padded: Vec<u8> = vec![0u8; crate::data::PAGE_SIZE * 2];
97 bytes_b_padded[..bytes_b.len()].copy_from_slice(bytes_b.as_slice());
98 let a: Cap = Cap::Data(DataCap {
99 content: DataContent::Inline(bytes_a_padded),
100 });
101 let b: Cap = Cap::Data(DataCap {
102 content: DataContent::Inline(bytes_b_padded),
103 });
104 assert_ne!(cap_hash(&a), cap_hash(&b));
105 }
106
107 #[test]
108 fn cnode_empty_vs_one_populated_differ() {
109 let empty: CNodeCap = CNodeCap::new(2).unwrap();
110 let mut populated: CNodeCap = CNodeCap::new(2).unwrap();
111 populated
112 .set(SlotIdx(0), Some(CapHashOrRef::Hash([0xEE; 32])))
113 .unwrap();
114 let a: Cap = Cap::CNode(empty);
115 let b: Cap = Cap::CNode(populated);
116 assert_ne!(cap_hash(&a), cap_hash(&b));
117 }
118
119 #[test]
120 fn cnode_with_ref_target_panics() {
121 use crate::cap::CapRef;
122 let mut cn: CNodeCap = CNodeCap::new(2).unwrap();
123 cn.set(SlotIdx(0), Some(CapHashOrRef::Ref(CapRef::new(42))))
124 .unwrap();
125 let cap: Cap = Cap::CNode(cn);
126 let result = std::panic::catch_unwind(|| cap_hash(&cap));
127 assert!(result.is_err());
128 }
129
130 #[test]
131 fn image_hash_depends_on_code() {
132 let mut img_a = empty_image();
133 let mut img_b = empty_image();
134 img_a.code.extend_from_slice(b"foo");
135 img_b.code.extend_from_slice(b"bar");
136 let a: Cap = Cap::Image(img_a);
137 let b: Cap = Cap::Image(img_b);
138 assert_ne!(cap_hash(&a), cap_hash(&b));
139 }
140
141 fn empty_image() -> ImageCap {
142 ImageCap {
143 code: Vec::new(),
144 bitmask: Vec::new(),
145 jump_table: Vec::new(),
146 endpoints: Vec::new(),
147 mappings: Vec::new(),
148 pinned: Vec::new(),
149 initial: Vec::new(),
150 yield_marker_slot: None,
151 }
152 }
153
154 #[test]
155 fn instance_hash_depends_on_pc() {
156 let mut inst_a = empty_instance();
157 let mut inst_b = empty_instance();
158 inst_a.pc = 0x100;
159 inst_b.pc = 0x200;
160 let a: Cap = Cap::Instance(inst_a);
161 let b: Cap = Cap::Instance(inst_b);
162 assert_ne!(cap_hash(&a), cap_hash(&b));
163 }
164
165 fn empty_instance() -> InstanceCap {
166 InstanceCap {
167 image_hash_chain: [0; 32],
168 image_hash: [0; 32],
169 root_cnode: CapHashOrRef::Hash([0; 32]),
170 rw_overlays: Vec::new(),
171 mem_size: 0,
172 regs: [0; crate::cap::NUM_REGS],
173 pc: 0,
174 gas_remaining: 0,
175 }
176 }
177
178 #[test]
179 fn data_paged_hash_uses_loaded_page_hashes() {
180 let mut bytes = Vec::new();
181 bytes.extend_from_slice(&[1, 2, 3]);
182 let pb_hash = [0xA1; 32];
183 let pb = PageBytes {
184 hash: pb_hash,
185 bytes,
186 };
187 let pr: PageRef = PageRef::new(pb);
188 let pages: Vec<PageSlot> = vec![PageSlot::Loaded(pr)];
189 let cap: Cap = Cap::Data(DataCap {
190 content: DataContent::Paged {
191 page_size: 4096,
192 pages,
193 },
194 });
195 let h = cap_hash(&cap);
196 let bytes2: Vec<u8> = vec![1, 2, 3];
198 let pb2 = PageBytes {
199 hash: [0xB2; 32],
200 bytes: bytes2,
201 };
202 let pr2: PageRef = PageRef::new(pb2);
203 let pages2: Vec<PageSlot> = vec![PageSlot::Loaded(pr2)];
204 let cap2: Cap = Cap::Data(DataCap {
205 content: DataContent::Paged {
206 page_size: 4096,
207 pages: pages2,
208 },
209 });
210 assert_ne!(h, cap_hash(&cap2));
211 }
212
213 #[test]
214 fn loaded_page_substitutes_for_missing_with_same_hash() {
215 let page_hash = [0xCD; 32];
218 let bytes: Vec<u8> = vec![0xAA; 16];
219 let pb = PageBytes {
220 hash: page_hash,
221 bytes,
222 };
223 let pr: PageRef = PageRef::new(pb);
224
225 let pages_loaded: Vec<PageSlot> = vec![PageSlot::Loaded(pr)];
226 let cap_loaded: Cap = Cap::Data(DataCap {
227 content: DataContent::Paged {
228 page_size: 16,
229 pages: pages_loaded,
230 },
231 });
232
233 let pages_missing: Vec<PageSlot> = vec![PageSlot::Missing(page_hash)];
234 let cap_missing: Cap = Cap::Data(DataCap {
235 content: DataContent::Paged {
236 page_size: 16,
237 pages: pages_missing,
238 },
239 });
240
241 assert_eq!(cap_hash(&cap_loaded), cap_hash(&cap_missing));
242 }
243}