1use std::{
2 collections::{vec_deque::Iter, VecDeque},
3 fmt::Debug,
4};
5
6use serde::{Deserialize, Serialize};
7
8#[cfg(any(test, feature = "fuzzing"))]
9use proptest_derive::Arbitrary;
10
11use diem_logger::prelude::*;
12
13use crate::{
14 block_info::View,
15 term_state::pos_state_config::{PosStateConfigTrait, POS_STATE_CONFIG},
16};
17
18#[derive(Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Debug)]
19#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
20pub struct StatusItem {
21 pub view: View,
22 pub votes: u64,
23}
24
25#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug)]
26#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
27pub struct StatusList {
28 inner: VecDeque<StatusItem>,
29 sorted: bool,
30}
31
32impl Default for StatusList {
33 fn default() -> Self {
34 Self {
35 inner: VecDeque::new(),
36 sorted: true,
37 }
38 }
39}
40
41impl StatusList {
42 fn push(
44 &mut self, exit_view: View, votes: u64, update_views: &mut Vec<View>,
45 ) {
46 if self
49 .inner
50 .back()
51 .map_or(false, |item| item.view > exit_view)
52 {
53 self.sorted = false;
54 }
55 self.inner.push_back(StatusItem {
56 view: exit_view,
57 votes,
58 });
59 update_views.push(exit_view);
60 }
61
62 fn pull(&mut self, required_votes: u64) -> Option<StatusItem> {
65 self.sort();
66 if let Some(item) = self.inner.pop_front() {
67 if item.votes <= required_votes {
68 Some(item)
69 } else {
70 let rest_votes = item.votes - required_votes;
71 self.inner.push_front(StatusItem {
72 view: item.view,
73 votes: rest_votes,
74 });
75 Some(StatusItem {
76 view: item.view,
77 votes: required_votes,
78 })
79 }
80 } else {
81 None
82 }
83 }
84
85 fn pop_by_view(&mut self, view: View) -> Option<StatusItem> {
87 self.sort();
88 if let Some(item) = self.inner.pop_front() {
89 if item.view > view {
90 self.inner.push_front(item);
91 None
92 } else {
93 Some(item)
94 }
95 } else {
96 None
97 }
98 }
99
100 fn sort(&mut self) {
101 if !self.sorted {
102 self.inner
103 .make_contiguous()
104 .sort_unstable_by_key(|item| item.view);
105 self.sorted = true;
106 }
107 }
108
109 fn clear(&mut self) {
110 self.inner.clear();
111 self.sorted = true;
112 }
113
114 pub fn len(&self) -> usize { self.inner.len() }
115
116 pub fn iter(&self) -> Iter<'_, StatusItem> { self.inner.iter() }
117}
118
119#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug, Default)]
120#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
121pub struct NodeLockStatus {
122 pub in_queue: StatusList,
123 pub locked: u64,
124 pub out_queue: StatusList,
125 unlocked: u64,
126
127 available_votes: u64,
129
130 force_retired: Option<View>,
132 exempt_from_forfeit: Option<u64>,
135}
136
137impl NodeLockStatus {
138 pub fn available_votes(&self) -> u64 {
139 if self.exempt_from_forfeit.is_some() {
140 0
141 } else {
142 self.available_votes
143 }
144 }
145
146 pub fn unlocked_votes(&self) -> u64 {
147 self.exempt_from_forfeit.unwrap_or(self.unlocked)
148 }
149
150 pub fn forfeited(&self) -> u64 { self.unlocked - self.unlocked_votes() }
151
152 pub fn force_retired(&self) -> Option<u64> { self.force_retired }
153
154 pub fn exempt_from_forfeit(&self) -> Option<u64> {
155 self.exempt_from_forfeit
156 }
157}
158
159impl NodeLockStatus {
160 pub(super) fn update(&mut self, view: View) -> bool {
161 let mut new_votes_unlocked = false;
162
163 while let Some(item) = self.in_queue.pop_by_view(view) {
164 self.locked += item.votes;
165 }
166
167 while let Some(item) = self.out_queue.pop_by_view(view) {
168 self.unlocked += item.votes;
169 new_votes_unlocked = true;
170 }
171
172 if self.force_retired.map_or(false, |retire_view| {
173 view >= retire_view
174 + POS_STATE_CONFIG.force_retired_locked_views(view)
175 }) {
176 self.force_retired = None;
177 }
178
179 if self.exempt_from_forfeit.is_some() {
180 new_votes_unlocked = false
181 }
182
183 new_votes_unlocked
184 }
185
186 pub(super) fn new_lock(
187 &mut self, view: View, votes: u64, initialize_mode: bool,
188 update_views: &mut Vec<View>,
189 ) {
190 if votes == 0 {
191 return;
192 }
193
194 if initialize_mode {
195 self.available_votes += votes;
196 self.locked += votes;
197 return;
198 }
199
200 if self.force_retired.is_some() {
203 let exit_view = view
204 + POS_STATE_CONFIG.in_queue_locked_views(view)
205 + POS_STATE_CONFIG.out_queue_locked_views(view);
206 self.out_queue.push(exit_view, votes, update_views);
207 } else {
208 self.available_votes += votes;
209 let exit_view = view + POS_STATE_CONFIG.in_queue_locked_views(view);
210 self.in_queue.push(exit_view, votes, update_views);
211 }
212 }
213
214 pub(super) fn new_unlock(
215 &mut self, view: View, to_unlock_votes: u64,
216 update_views: &mut Vec<View>,
217 ) {
218 if to_unlock_votes == 0 {
219 return;
220 }
221
222 let before_available_votes = self.available_votes;
223 let mut rest_votes = to_unlock_votes;
224
225 let votes = rest_votes.min(self.locked);
227 if votes > 0 {
228 rest_votes -= votes;
229 self.locked -= votes;
230 self.available_votes -= votes;
231
232 let exit_view =
233 view + POS_STATE_CONFIG.out_queue_locked_views(view);
234 self.out_queue.push(exit_view, votes, update_views);
235 }
236
237 while rest_votes > 0 {
239 let maybe_item = self.in_queue.pull(rest_votes);
240
241 if maybe_item.is_none() {
242 diem_warn!(
243 "Not enough votes to unlock: before available votes {}, to unlock votes {}, rest votes {}.",
244 before_available_votes,
245 to_unlock_votes,
246 rest_votes
247 );
248 break;
249 }
250
251 let item = maybe_item.unwrap();
252
253 rest_votes -= item.votes;
254 self.available_votes -= item.votes;
255
256 let exit_view =
257 item.view + POS_STATE_CONFIG.out_queue_locked_views(view);
258 self.out_queue.push(exit_view, item.votes, update_views);
259 }
260 }
261
262 pub(super) fn force_retire(
263 &mut self, view: View, callback_views: &mut Vec<View>,
264 ) {
265 if self.force_retired.is_none() {
266 self.force_retired = Some(view);
267 callback_views
268 .push(view + POS_STATE_CONFIG.force_retired_locked_views(view));
269 self.new_unlock(view, self.available_votes, callback_views);
270 }
271 }
272
273 pub(super) fn forfeit(
274 &mut self, view: View, updated_views: &mut Vec<View>,
275 ) {
276 if self.exempt_from_forfeit.is_some() {
277 return;
278 }
279 match POS_STATE_CONFIG.dispute_locked_views(view) {
280 None => self.exempt_from_forfeit = Some(self.unlocked),
281 Some(dispute_locked_views) => {
282 if self.available_votes > 0 {
283 let mut to_lock_votes = self.available_votes;
286 self.in_queue.clear();
287 self.locked = 0;
288 self.available_votes = 0;
289
290 while let Some(item) = self.out_queue.pop_by_view(u64::MAX)
291 {
292 to_lock_votes += item.votes;
293 }
294 self.force_retired = None;
298
299 self.out_queue.push(
300 view.saturating_add(dispute_locked_views),
301 to_lock_votes,
302 updated_views,
303 );
304 }
305 }
306 }
307 }
308}
309
310#[allow(dead_code)]
311pub mod tests {
312 use super::*;
313 use std::collections::HashSet;
314
315 enum Operation {
316 NewLock(u64),
317 NewUnlock(u64),
318 ForceRetire,
319 Forfeit,
320 AssertAvailable(u64),
321 AssertLocked(u64),
322 AssertUnlocked(u64),
323 }
324
325 use Operation::*;
326
327 fn run_tasks(tasks: Vec<(Operation, View)>) {
328 let mut tasks: VecDeque<(Operation, View)> = tasks.into();
329
330 let mut lock_status = NodeLockStatus::default();
331 let mut hint_views = HashSet::<View>::new();
332 let mut view = 0;
333
334 while !(tasks.is_empty() && hint_views.is_empty()) {
335 if hint_views.contains(&view) {
336 lock_status.update(view);
337 hint_views.remove(&view);
338 }
339
340 let mut update_views = Vec::new();
341
342 while tasks.front().map(|x| x.1) == Some(view) {
343 match tasks.pop_front().unwrap().0 {
344 Operation::NewLock(votes) => {
345 lock_status.new_lock(
346 view,
347 votes,
348 false,
349 &mut update_views,
350 );
351 }
352 Operation::NewUnlock(votes) => {
353 lock_status.new_unlock(view, votes, &mut update_views);
354 }
355 Operation::ForceRetire => {
356 lock_status.force_retire(view, &mut update_views);
357 }
358 Operation::Forfeit => {
359 lock_status.forfeit(view, &mut update_views)
360 }
361 Operation::AssertAvailable(votes) => {
362 if lock_status.available_votes != votes {
363 panic!("View {}\n {:?}", view, lock_status);
364 }
365 }
366 Operation::AssertLocked(votes) => {
367 if lock_status.locked != votes {
368 panic!("View {}\n {:?}", view, lock_status);
369 }
370 }
371 Operation::AssertUnlocked(votes) => {
372 if lock_status.unlocked_votes() != votes {
373 panic!("View {}\n {:?}", view, lock_status);
374 }
375 }
376 }
377 }
378
379 for update_view in update_views {
380 if update_view > view {
381 hint_views.insert(update_view);
382 }
383 }
384 view += 1;
385 }
386 }
387
388 fn basic() {
390 let one_vote = vec![
391 (NewLock(1), 2),
392 (AssertAvailable(1), 3),
393 (AssertLocked(1), 10082),
394 (NewUnlock(1), 20000),
395 (AssertAvailable(0), 20001),
396 (AssertUnlocked(0), 20002),
397 (AssertUnlocked(1), 30080),
398 ];
399
400 let multi_vote = vec![
401 (NewLock(10), 2u64),
402 (AssertAvailable(10), 3),
403 (AssertLocked(10), 10082),
404 (NewUnlock(7), 20000),
405 (AssertAvailable(3), 20001),
406 (AssertUnlocked(0), 20002),
407 (AssertUnlocked(7), 30080),
408 (AssertAvailable(3), 30081),
409 ];
410
411 run_tasks(one_vote);
412 run_tasks(multi_vote);
413 }
414
415 fn increase_during_exit() {
417 let tasks = vec![
418 (NewLock(10), 2),
419 (AssertAvailable(10), 3),
420 (NewLock(5), 4),
421 (AssertAvailable(15), 5),
422 (NewUnlock(7), 20000),
423 (AssertAvailable(8), 20001),
424 (NewLock(5), 20002),
425 (AssertAvailable(13), 20003),
426 (NewUnlock(7), 20004),
427 (AssertAvailable(6), 20005),
428 (AssertUnlocked(0), 20005),
429 (NewUnlock(3), 20006),
430 (AssertUnlocked(7), 30080),
431 (AssertUnlocked(14), 30084),
432 (AssertUnlocked(15), 30086),
433 (AssertUnlocked(15), 40161),
434 (AssertUnlocked(17), 40162),
435 ];
436
437 run_tasks(tasks);
438 }
439
440 fn force_retire() {
442 let tasks = vec![
443 (NewLock(6), 2),
444 (AssertAvailable(6), 3),
445 (NewLock(7), 12),
446 (AssertAvailable(13), 13),
447 (AssertLocked(6), 10090),
448 (ForceRetire, 10090),
449 (NewLock(8), 10092),
450 (AssertAvailable(0), 10093),
451 (AssertLocked(0), 10093),
452 (AssertUnlocked(0), 20169),
453 (AssertUnlocked(6), 20170),
454 (NewLock(9), 20170),
455 (AssertAvailable(9), 20171),
456 (AssertUnlocked(13), 20172),
457 (AssertLocked(0), 30249),
458 (AssertLocked(9), 30250),
459 (AssertUnlocked(13), 30251),
460 (AssertUnlocked(21), 30252),
461 ];
462
463 run_tasks(tasks);
464 }
465
466 fn resolve_retired() {
467 let tasks = vec![
468 (NewLock(6), 2),
469 (AssertAvailable(6), 3),
470 (ForceRetire, 10),
471 (NewLock(8), 10090),
472 (AssertAvailable(8), 10091),
473 ];
474
475 run_tasks(tasks);
476 }
477
478 pub fn run_all() {
479 basic();
480 increase_during_exit();
481 force_retire();
482 resolve_retired();
483 }
484}