1use crate::verify::verify_attestation_parts;
8use crate::{
9 bitmap::{bitmap_load, for_each_set_bit_u256},
10 secp256k1_scalar_is_valid_nonzero, verify_aggregate_over_hash, AttestationError,
11 AttestationPayload, NodeEntry, RegistryTransitionType, RegistryView, SchnorrSignature,
12 SignerXy, VIRTUAL_INDEX,
13};
14
15#[inline(always)]
16pub fn expected_node_index(bit_pos: u32, registry: &RegistryView, apply_remove_remap: bool) -> u32 {
17 if !apply_remove_remap {
18 return bit_pos;
19 }
20 if bit_pos == registry.removed_old_index {
21 VIRTUAL_INDEX
22 } else if registry.last_transition_type == RegistryTransitionType::RemoveSwap
23 && bit_pos == registry.moved_old_index
24 {
25 registry.removed_old_index
26 } else {
27 bit_pos
28 }
29}
30
31#[inline(always)]
32pub fn validate_remove_transition_for_previous(
33 registry: &RegistryView,
34) -> Result<(), AttestationError> {
35 if registry.is_remove_transition() {
36 Ok(())
37 } else {
38 Err(AttestationError::InvalidTransitionAccount)
39 }
40}
41
42pub fn resolve_ordered_signers(
49 entries: &[NodeEntry],
50 registry: &RegistryView,
51 registry_version: u32,
52 signers_bitmap: &[u8; 32],
53 now: i64,
54) -> Result<(u32, Vec<SignerXy>), AttestationError> {
55 let signers = bitmap_load(signers_bitmap);
56 let signer_count = signers.count_ones();
57
58 let is_current = registry_version == registry.current_version;
59 let is_previous_live =
60 registry_version == registry.previous_version && now <= registry.previous_expires_at;
61 if !is_current && !is_previous_live {
62 return Err(AttestationError::InvalidRegistryVersion);
63 }
64
65 let node_count = if is_current {
66 registry.current_node_count
67 } else {
68 registry.previous_node_count
69 };
70
71 if entries.len() != signer_count as usize {
72 return Err(AttestationError::MissingSignerAccount);
73 }
74
75 let apply_remove_remap =
76 !is_current && registry.last_transition_type != RegistryTransitionType::Add;
77 if apply_remove_remap {
78 validate_remove_transition_for_previous(registry)?;
79 }
80
81 let mut ordered = Vec::with_capacity(signer_count as usize);
82 let mut entry_cursor = 0usize;
83 for_each_set_bit_u256(signers, |bit_pos| {
84 let bit_pos = bit_pos as u32;
85 if bit_pos >= node_count {
86 return Err(AttestationError::InvalidSignersBitmap);
87 }
88
89 let entry = entries
90 .get(entry_cursor)
91 .ok_or(AttestationError::MissingSignerAccount)?;
92 entry_cursor = entry_cursor.saturating_add(1);
93
94 let expected_index = expected_node_index(bit_pos, registry, apply_remove_remap);
95 if entry.index != expected_index {
96 return Err(AttestationError::InvalidNodeIndex);
97 }
98 ordered.push((entry.x, entry.y));
99 Ok(())
100 })?;
101
102 if entry_cursor != entries.len() {
103 return Err(AttestationError::MissingSignerAccount);
104 }
105 Ok((node_count, ordered))
106}
107
108#[allow(clippy::too_many_arguments)]
109pub fn verify_attestation_resolved(
110 payload: &AttestationPayload,
111 signature: &SchnorrSignature,
112 registry: &RegistryView,
113 redundancy_buffer: u8,
114 now: i64,
115 entries: &[NodeEntry],
116) -> Result<(), AttestationError> {
117 let (node_count, ordered) = resolve_ordered_signers(
118 entries,
119 registry,
120 payload.registry_version,
121 &signature.signers_bitmap,
122 now,
123 )?;
124 verify_attestation_parts(payload, signature, node_count, redundancy_buffer, &ordered)
125}
126
127#[allow(clippy::too_many_arguments)]
128pub fn verify_aggregate_over_hash_resolved(
129 registry: &RegistryView,
130 registry_version: u32,
131 signers_bitmap: &[u8; 32],
132 agg_sig_s: &[u8; 32],
133 commitment_addr: &[u8; 20],
134 message_hash: &[u8; 32],
135 now: i64,
136 entries: &[NodeEntry],
137) -> Result<bool, AttestationError> {
138 if !secp256k1_scalar_is_valid_nonzero(agg_sig_s) {
139 return Ok(false);
140 }
141 let (_, ordered) =
142 resolve_ordered_signers(entries, registry, registry_version, signers_bitmap, now)?;
143 verify_aggregate_over_hash(&ordered, agg_sig_s, commitment_addr, message_hash)
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 fn remove_swap_registry() -> RegistryView {
151 RegistryView {
152 current_version: 2,
153 previous_version: 1,
154 previous_expires_at: 9_999,
155 current_node_count: 7,
156 previous_node_count: 8,
157 last_transition_type: RegistryTransitionType::RemoveSwap,
158 removed_old_index: 1,
159 moved_old_index: 3,
160 }
161 }
162
163 #[test]
164 fn expected_node_index_remove_swap_remaps_removed_and_moved() {
165 let registry = remove_swap_registry();
166 assert_eq!(expected_node_index(1, ®istry, true), VIRTUAL_INDEX);
167 assert_eq!(expected_node_index(3, ®istry, true), 1);
168 assert_eq!(expected_node_index(0, ®istry, true), 0);
169 }
170
171 #[test]
172 fn expected_node_index_remove_tail_remaps_removed_only() {
173 let mut registry = remove_swap_registry();
174 registry.last_transition_type = RegistryTransitionType::RemoveTail;
175 registry.removed_old_index = 2;
176 registry.moved_old_index = VIRTUAL_INDEX;
177 assert_eq!(expected_node_index(2, ®istry, true), VIRTUAL_INDEX);
178 assert_eq!(expected_node_index(0, ®istry, true), 0);
179 }
180
181 #[test]
182 fn expected_node_index_without_remove_remap_is_identity() {
183 let registry = remove_swap_registry();
184 assert_eq!(expected_node_index(4, ®istry, false), 4);
185 }
186
187 #[test]
188 fn resolve_ordered_signers_rejects_out_of_range_bit_on_add_previous() {
189 let registry = RegistryView {
190 current_version: 2,
191 previous_version: 1,
192 previous_expires_at: 9_999,
193 current_node_count: 6,
194 previous_node_count: 5,
195 last_transition_type: RegistryTransitionType::Add,
196 removed_old_index: VIRTUAL_INDEX,
197 moved_old_index: VIRTUAL_INDEX,
198 };
199 let mut signers_bitmap = [0u8; 32];
200 signers_bitmap[0] = 1 << 5;
201
202 let entries = [NodeEntry {
203 index: 5,
204 x: [1u8; 32],
205 y: [2u8; 32],
206 }];
207
208 let err = resolve_ordered_signers(
209 &entries,
210 ®istry,
211 registry.previous_version,
212 &signers_bitmap,
213 0,
214 )
215 .unwrap_err();
216 assert_eq!(err, AttestationError::InvalidSignersBitmap);
217 }
218}