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