1use omena_refinement_trait::{
4 RefinementVerdictV0, RefinementWitnessV0, refinement_provenance_v0, refinement_witness_v0,
5};
6
7use crate::{
8 CascadeDeclaration, CascadeLevel, LayerFlattenInputV0, ScopeFlattenInputV0,
9 StaticSupportsAssumptionV0, StaticSupportsEvalVerdictV0, evaluate_static_supports_condition,
10 prove_layer_flatten_candidate, prove_scope_flatten_candidate,
11};
12
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct CascadeRefinementContextV0 {
15 pub supports_condition: Option<String>,
16 pub scope_root_selector: Option<String>,
17 pub layer_name: Option<String>,
18 pub closed_bundle: bool,
19}
20
21impl Default for CascadeRefinementContextV0 {
22 fn default() -> Self {
23 Self {
24 supports_condition: None,
25 scope_root_selector: None,
26 layer_name: None,
27 closed_bundle: true,
28 }
29 }
30}
31
32pub fn refine_declaration_in_context(
33 declaration: &CascadeDeclaration,
34 context: &CascadeRefinementContextV0,
35) -> RefinementWitnessV0 {
36 let mut provenances = Vec::new();
37 let mut verdicts = Vec::new();
38
39 if let Some(condition) = context.supports_condition.as_deref() {
40 let supports = evaluate_static_supports_condition(
41 condition,
42 StaticSupportsAssumptionV0::ModernBrowser,
43 );
44 provenances.push(refinement_provenance_v0(
45 "supports-predicate",
46 Some("evaluate_static_supports_condition"),
47 ));
48 verdicts.push(match supports.verdict {
49 StaticSupportsEvalVerdictV0::AlwaysTrue => RefinementVerdictV0::SatisfiedAll,
50 StaticSupportsEvalVerdictV0::AlwaysFalse => RefinementVerdictV0::Unsatisfiable,
51 StaticSupportsEvalVerdictV0::Unknown => RefinementVerdictV0::Unknown,
52 });
53 }
54
55 if let Some(root_selector) = context.scope_root_selector.as_deref() {
56 let scope = prove_scope_flatten_candidate(ScopeFlattenInputV0 {
57 root_selector: root_selector.to_string(),
58 limit_selector: None,
59 scoped_rule_count: 1,
60 peer_scope_count: 0,
61 competing_unscoped_rule_count: 0,
62 inside_layer: context.layer_name.is_some(),
63 });
64 provenances.push(refinement_provenance_v0(
65 "scope-predicate",
66 Some("prove_scope_flatten_candidate"),
67 ));
68 verdicts.push(if scope.accepted {
69 RefinementVerdictV0::SatisfiedAll
70 } else {
71 RefinementVerdictV0::Unknown
72 });
73 }
74
75 if context.layer_name.is_some() {
76 let layer = prove_layer_flatten_candidate(LayerFlattenInputV0 {
77 layer_name: context.layer_name.clone(),
78 layer_rule_count: 1,
79 peer_layer_count: 0,
80 unlayered_rule_count: 0,
81 important_declaration_count: usize::from(matches!(
82 declaration.key.level,
83 CascadeLevel::AuthorImportant
84 | CascadeLevel::UserImportant
85 | CascadeLevel::UserAgentImportant
86 )),
87 closed_bundle: context.closed_bundle,
88 });
89 provenances.push(refinement_provenance_v0(
90 "layer-predicate",
91 Some("prove_layer_flatten_candidate"),
92 ));
93 verdicts.push(if layer.accepted {
94 RefinementVerdictV0::SatisfiedAll
95 } else {
96 RefinementVerdictV0::Unknown
97 });
98 }
99
100 let verdict = combine_refinement_verdicts(&verdicts);
101 refinement_witness_v0("cascade-refinement-conjunction", verdict, provenances)
102}
103
104fn combine_refinement_verdicts(verdicts: &[RefinementVerdictV0]) -> RefinementVerdictV0 {
105 if verdicts.is_empty() {
106 return RefinementVerdictV0::SatisfiedAll;
107 }
108 if verdicts.contains(&RefinementVerdictV0::Unsatisfiable) {
109 return RefinementVerdictV0::Unsatisfiable;
110 }
111 if verdicts
112 .iter()
113 .all(|verdict| *verdict == RefinementVerdictV0::SatisfiedAll)
114 {
115 return RefinementVerdictV0::SatisfiedAll;
116 }
117 if verdicts.contains(&RefinementVerdictV0::SatisfiedAll) {
118 RefinementVerdictV0::SatisfiedSome
119 } else {
120 RefinementVerdictV0::Unknown
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 const EXPECTED_LEGACY_PROOFS_RS_SHA256: [u8; 32] = [
127 0x24, 0xa4, 0x02, 0x86, 0x46, 0x88, 0xe9, 0xcf, 0x2e, 0x1a, 0x38, 0xe6, 0xc9, 0x20, 0x31,
128 0xfa, 0xb0, 0xa4, 0x2a, 0x20, 0x16, 0x23, 0x55, 0x24, 0x59, 0x0a, 0x89, 0xc7, 0x70, 0x3c,
129 0x65, 0x17,
130 ];
131
132 #[test]
133 fn legacy_proofs_rs_byte_untouched() {
134 let digest = sha256(include_bytes!("proofs.rs"));
135 assert_eq!(digest, EXPECTED_LEGACY_PROOFS_RS_SHA256);
136 }
137
138 fn sha256(input: &[u8]) -> [u8; 32] {
139 const H0: [u32; 8] = [
140 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
141 0x5be0cd19,
142 ];
143 const K: [u32; 64] = [
144 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,
145 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe,
146 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f,
147 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
148 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
149 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
150 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116,
151 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
152 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
153 0xc67178f2,
154 ];
155
156 let mut bytes = input.to_vec();
157 let bit_len = (bytes.len() as u64) * 8;
158 bytes.push(0x80);
159 while bytes.len() % 64 != 56 {
160 bytes.push(0);
161 }
162 bytes.extend_from_slice(&bit_len.to_be_bytes());
163
164 let mut state = H0;
165 for chunk in bytes.chunks_exact(64) {
166 let mut w = [0u32; 64];
167 for (index, word) in chunk.chunks_exact(4).enumerate() {
168 w[index] = u32::from_be_bytes([word[0], word[1], word[2], word[3]]);
169 }
170 for index in 16..64 {
171 let s0 = w[index - 15].rotate_right(7)
172 ^ w[index - 15].rotate_right(18)
173 ^ (w[index - 15] >> 3);
174 let s1 = w[index - 2].rotate_right(17)
175 ^ w[index - 2].rotate_right(19)
176 ^ (w[index - 2] >> 10);
177 w[index] = w[index - 16]
178 .wrapping_add(s0)
179 .wrapping_add(w[index - 7])
180 .wrapping_add(s1);
181 }
182
183 let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h] = state;
184 for index in 0..64 {
185 let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
186 let ch = (e & f) ^ ((!e) & g);
187 let temp1 = h
188 .wrapping_add(s1)
189 .wrapping_add(ch)
190 .wrapping_add(K[index])
191 .wrapping_add(w[index]);
192 let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
193 let maj = (a & b) ^ (a & c) ^ (b & c);
194 let temp2 = s0.wrapping_add(maj);
195 h = g;
196 g = f;
197 f = e;
198 e = d.wrapping_add(temp1);
199 d = c;
200 c = b;
201 b = a;
202 a = temp1.wrapping_add(temp2);
203 }
204
205 for (slot, value) in state.iter_mut().zip([a, b, c, d, e, f, g, h]) {
206 *slot = slot.wrapping_add(value);
207 }
208 }
209
210 let mut digest = [0u8; 32];
211 for (chunk, word) in digest.chunks_exact_mut(4).zip(state) {
212 chunk.copy_from_slice(&word.to_be_bytes());
213 }
214 digest
215 }
216}