pallet_staking/
election_size_tracker.rs1use codec::Encode;
74use frame_election_provider_support::{
75 bounds::{DataProviderBounds, SizeBound},
76 ElectionDataProvider, VoterOf,
77};
78
79#[derive(Clone, Copy, Debug)]
84pub struct StaticTracker<DataProvider> {
85 pub size: usize,
86 pub counter: usize,
87 _marker: core::marker::PhantomData<DataProvider>,
88}
89
90impl<DataProvider> Default for StaticTracker<DataProvider> {
91 fn default() -> Self {
92 Self { size: 0, counter: 0, _marker: Default::default() }
93 }
94}
95
96impl<DataProvider> StaticTracker<DataProvider>
97where
98 DataProvider: ElectionDataProvider,
99{
100 pub fn try_register_voter(
105 &mut self,
106 voter: &VoterOf<DataProvider>,
107 bounds: &DataProviderBounds,
108 ) -> Result<(), ()> {
109 let tracker_size_after = {
110 let voter_hint = Self::voter_size_hint(voter);
111 Self::final_byte_size_of(self.counter + 1, self.size.saturating_add(voter_hint))
112 };
113
114 match bounds.size_exhausted(SizeBound(tracker_size_after as u32)) {
115 true => Err(()),
116 false => {
117 self.size = tracker_size_after;
118 self.counter += 1;
119 Ok(())
120 },
121 }
122 }
123
124 fn voter_size_hint(voter: &VoterOf<DataProvider>) -> usize {
126 let (voter_account, vote_weight, targets) = voter;
127
128 voter_account
129 .size_hint()
130 .saturating_add(vote_weight.size_hint())
131 .saturating_add(voter_account.size_hint().saturating_mul(targets.len()))
132 }
133
134 pub fn try_register_target(
139 &mut self,
140 target: DataProvider::AccountId,
141 bounds: &DataProviderBounds,
142 ) -> Result<(), ()> {
143 let tracker_size_after = Self::final_byte_size_of(
144 self.counter + 1,
145 self.size.saturating_add(target.size_hint()),
146 );
147
148 match bounds.size_exhausted(SizeBound(tracker_size_after as u32)) {
149 true => Err(()),
150 false => {
151 self.size = tracker_size_after;
152 self.counter += 1;
153 Ok(())
154 },
155 }
156 }
157
158 #[inline]
160 fn length_prefix(len: usize) -> usize {
161 use codec::{Compact, CompactLen};
162 Compact::<u32>::compact_len(&(len as u32))
163 }
164
165 fn final_byte_size_of(num_voters: usize, size: usize) -> usize {
167 Self::length_prefix(num_voters).saturating_add(size)
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174 use crate::{
175 mock::{AccountId, Staking, Test},
176 BoundedVec, MaxNominationsOf,
177 };
178 use frame_election_provider_support::bounds::ElectionBoundsBuilder;
179 use sp_core::bounded_vec;
180
181 type Voters = BoundedVec<AccountId, MaxNominationsOf<Test>>;
182
183 #[test]
184 pub fn election_size_tracker_works() {
185 let mut voters: Vec<(u64, u64, Voters)> = vec![];
186 let mut size_tracker = StaticTracker::<Staking>::default();
187 let voter_bounds = ElectionBoundsBuilder::default().voters_size(1_50.into()).build().voters;
188
189 let voter = (1, 10, bounded_vec![2]);
191 assert!(size_tracker.try_register_voter(&voter, &voter_bounds).is_ok());
192 voters.push(voter);
193
194 assert_eq!(
195 StaticTracker::<Staking>::final_byte_size_of(size_tracker.counter, size_tracker.size),
196 voters.encoded_size()
197 );
198
199 let voter = (2, 20, bounded_vec![3, 4, 5]);
201 assert!(size_tracker.try_register_voter(&voter, &voter_bounds).is_ok());
202 voters.push(voter);
203
204 assert_eq!(
205 StaticTracker::<Staking>::final_byte_size_of(size_tracker.counter, size_tracker.size),
206 voters.encoded_size()
207 );
208
209 let voter = (3, 30, bounded_vec![]);
211 assert!(size_tracker.try_register_voter(&voter, &voter_bounds).is_ok());
212 voters.push(voter);
213
214 assert_eq!(
215 StaticTracker::<Staking>::final_byte_size_of(size_tracker.counter, size_tracker.size),
216 voters.encoded_size()
217 );
218 }
219
220 #[test]
221 pub fn election_size_tracker_bounds_works() {
222 let mut voters: Vec<(u64, u64, Voters)> = vec![];
223 let mut size_tracker = StaticTracker::<Staking>::default();
224 let voter_bounds = ElectionBoundsBuilder::default().voters_size(1_00.into()).build().voters;
225
226 let voter = (1, 10, bounded_vec![2]);
227 assert!(size_tracker.try_register_voter(&voter, &voter_bounds).is_ok());
228 voters.push(voter);
229
230 assert_eq!(
231 StaticTracker::<Staking>::final_byte_size_of(size_tracker.counter, size_tracker.size),
232 voters.encoded_size()
233 );
234
235 assert!(size_tracker.size > 0 && size_tracker.size < 1_00);
236 let size_before_overflow = size_tracker.size;
237
238 let voter = (2, 10, bounded_vec![2, 3, 4, 5, 6, 7, 8, 9]);
240 voters.push(voter.clone());
241
242 assert!(size_tracker.try_register_voter(&voter, &voter_bounds).is_err());
243 assert!(size_tracker.size > 0 && size_tracker.size < 1_00);
244
245 assert_eq!(size_tracker.size, size_before_overflow);
247 }
248
249 #[test]
250 fn len_prefix_works() {
251 let length_samples =
252 vec![0usize, 1, 62, 63, 64, 16383, 16384, 16385, 1073741822, 1073741823, 1073741824];
253
254 for s in length_samples {
255 assert_eq!(vec![1u8; s].encoded_size(), StaticTracker::<Staking>::length_prefix(s) + s);
257 }
258 }
259}