polkadot_node_subsystem_util/
availability_chunks.rs1use polkadot_erasure_coding::systematic_recovery_threshold;
18use polkadot_primitives::{node_features, ChunkIndex, CoreIndex, NodeFeatures, ValidatorIndex};
19
20pub fn availability_chunk_index(
25 node_features: &NodeFeatures,
26 n_validators: usize,
27 core_index: CoreIndex,
28 validator_index: ValidatorIndex,
29) -> Result<ChunkIndex, polkadot_erasure_coding::Error> {
30 if node_features::FeatureIndex::AvailabilityChunkMapping.is_set(node_features) {
31 let systematic_threshold = systematic_recovery_threshold(n_validators)? as u32;
32 let core_start_pos = core_index.0 * systematic_threshold;
33
34 return Ok(ChunkIndex((core_start_pos + validator_index.0) % n_validators as u32));
35 }
36
37 Ok(validator_index.into())
38}
39
40pub fn availability_chunk_indices(
46 node_features: &NodeFeatures,
47 n_validators: usize,
48 core_index: CoreIndex,
49) -> Result<Vec<ChunkIndex>, polkadot_erasure_coding::Error> {
50 let identity = (0..n_validators).map(|index| ChunkIndex(index as u32));
51 if node_features
52 .get(usize::from(node_features::FeatureIndex::AvailabilityChunkMapping as u8))
53 .map(|bitref| *bitref)
54 .unwrap_or_default()
55 {
56 let systematic_threshold = systematic_recovery_threshold(n_validators)? as u32;
57 let core_start_pos = core_index.0 * systematic_threshold;
58
59 return Ok(identity
60 .into_iter()
61 .cycle()
62 .skip(core_start_pos as usize)
63 .take(n_validators)
64 .collect());
65 }
66
67 Ok(identity.collect())
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use std::collections::HashSet;
74
75 pub fn node_features_with_mapping_enabled() -> NodeFeatures {
76 let mut node_features = NodeFeatures::new();
77 node_features
78 .resize(node_features::FeatureIndex::AvailabilityChunkMapping as usize + 1, false);
79 node_features
80 .set(node_features::FeatureIndex::AvailabilityChunkMapping as u8 as usize, true);
81 node_features
82 }
83
84 pub fn node_features_with_other_bits_enabled() -> NodeFeatures {
85 let mut node_features = NodeFeatures::new();
86 node_features.resize(node_features::FeatureIndex::FirstUnassigned as usize + 1, true);
87 node_features
88 .set(node_features::FeatureIndex::AvailabilityChunkMapping as u8 as usize, false);
89 node_features
90 }
91
92 #[test]
93 fn test_availability_chunk_indices() {
94 let n_validators = 20u32;
95 let n_cores = 15u32;
96
97 {
99 for node_features in [NodeFeatures::EMPTY, node_features_with_other_bits_enabled()] {
100 for core_index in 0..n_cores {
101 let indices = availability_chunk_indices(
102 node_features.as_ref(),
103 n_validators as usize,
104 CoreIndex(core_index),
105 )
106 .unwrap();
107
108 for validator_index in 0..n_validators {
109 assert_eq!(
110 indices[validator_index as usize],
111 availability_chunk_index(
112 node_features.as_ref(),
113 n_validators as usize,
114 CoreIndex(core_index),
115 ValidatorIndex(validator_index)
116 )
117 .unwrap()
118 )
119 }
120
121 assert_eq!(
122 indices,
123 (0..n_validators).map(|i| ChunkIndex(i)).collect::<Vec<_>>()
124 );
125 }
126 }
127 }
128
129 {
131 let node_features = node_features_with_mapping_enabled();
132 let mut previous_indices = None;
133
134 for core_index in 0..n_cores {
135 let indices = availability_chunk_indices(
136 &node_features,
137 n_validators as usize,
138 CoreIndex(core_index),
139 )
140 .unwrap();
141
142 for validator_index in 0..n_validators {
143 assert_eq!(
144 indices[validator_index as usize],
145 availability_chunk_index(
146 &node_features,
147 n_validators as usize,
148 CoreIndex(core_index),
149 ValidatorIndex(validator_index)
150 )
151 .unwrap()
152 )
153 }
154
155 if let Some(previous_indices) = previous_indices {
157 assert_ne!(previous_indices, indices);
158 }
159
160 previous_indices = Some(indices.clone());
161
162 assert_eq!(
164 (0..n_validators).map(|i| ChunkIndex(i)).collect::<HashSet<_>>(),
165 indices.into_iter().collect::<HashSet<_>>()
166 );
167 }
168 }
169 }
170
171 #[test]
172 fn prevent_changes_to_mapping() {
175 let n_validators = 7;
176 let node_features = node_features_with_mapping_enabled();
177
178 assert_eq!(
179 availability_chunk_indices(&node_features, n_validators, CoreIndex(0))
180 .unwrap()
181 .into_iter()
182 .map(|i| i.0)
183 .collect::<Vec<u32>>(),
184 vec![0, 1, 2, 3, 4, 5, 6]
185 );
186 assert_eq!(
187 availability_chunk_indices(&node_features, n_validators, CoreIndex(1))
188 .unwrap()
189 .into_iter()
190 .map(|i| i.0)
191 .collect::<Vec<u32>>(),
192 vec![2, 3, 4, 5, 6, 0, 1]
193 );
194 assert_eq!(
195 availability_chunk_indices(&node_features, n_validators, CoreIndex(2))
196 .unwrap()
197 .into_iter()
198 .map(|i| i.0)
199 .collect::<Vec<u32>>(),
200 vec![4, 5, 6, 0, 1, 2, 3]
201 );
202 assert_eq!(
203 availability_chunk_indices(&node_features, n_validators, CoreIndex(3))
204 .unwrap()
205 .into_iter()
206 .map(|i| i.0)
207 .collect::<Vec<u32>>(),
208 vec![6, 0, 1, 2, 3, 4, 5]
209 );
210 assert_eq!(
211 availability_chunk_indices(&node_features, n_validators, CoreIndex(4))
212 .unwrap()
213 .into_iter()
214 .map(|i| i.0)
215 .collect::<Vec<u32>>(),
216 vec![1, 2, 3, 4, 5, 6, 0]
217 );
218 }
219}