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