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 is_empty(&self) -> bool { self.inner.is_empty() }
117
118 pub fn iter(&self) -> Iter<'_, StatusItem> { self.inner.iter() }
119}
120
121#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug, Default)]
122#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
123pub struct NodeLockStatus {
124 pub in_queue: StatusList,
125 pub locked: u64,
126 pub out_queue: StatusList,
127 unlocked: u64,
128
129 available_votes: u64,
131
132 force_retired: Option<View>,
134 exempt_from_forfeit: Option<u64>,
137}
138
139impl NodeLockStatus {
140 pub fn available_votes(&self) -> u64 {
141 if self.exempt_from_forfeit.is_some() {
142 0
143 } else {
144 self.available_votes
145 }
146 }
147
148 pub fn unlocked_votes(&self) -> u64 {
149 self.exempt_from_forfeit.unwrap_or(self.unlocked)
150 }
151
152 pub fn forfeited(&self) -> u64 { self.unlocked - self.unlocked_votes() }
153
154 pub fn force_retired(&self) -> Option<u64> { self.force_retired }
155
156 pub fn exempt_from_forfeit(&self) -> Option<u64> {
157 self.exempt_from_forfeit
158 }
159}
160
161impl NodeLockStatus {
162 pub(super) fn update(&mut self, view: View) -> bool {
163 let mut new_votes_unlocked = false;
164
165 while let Some(item) = self.in_queue.pop_by_view(view) {
166 self.locked += item.votes;
167 }
168
169 while let Some(item) = self.out_queue.pop_by_view(view) {
170 self.unlocked += item.votes;
171 new_votes_unlocked = true;
172 }
173
174 if self.force_retired.map_or(false, |retire_view| {
175 view >= retire_view
176 + POS_STATE_CONFIG.force_retired_locked_views(view)
177 }) {
178 self.force_retired = None;
179 }
180
181 if self.exempt_from_forfeit.is_some() {
182 new_votes_unlocked = false
183 }
184
185 new_votes_unlocked
186 }
187
188 pub(super) fn new_lock(
189 &mut self, view: View, votes: u64, initialize_mode: bool,
190 dispute_lock_until: Option<View>, update_views: &mut Vec<View>,
191 ) {
192 if votes == 0 {
193 return;
194 }
195
196 if initialize_mode {
197 self.available_votes += votes;
198 self.locked += votes;
199 return;
200 }
201
202 if self.force_retired.is_some() || dispute_lock_until.is_some() {
205 let exit_view = (view
206 + POS_STATE_CONFIG.in_queue_locked_views(view)
207 + POS_STATE_CONFIG.out_queue_locked_views(view))
208 .max(dispute_lock_until.unwrap_or(0));
209 self.out_queue.push(exit_view, votes, update_views);
210 } else {
211 self.available_votes += votes;
212 let exit_view = view + POS_STATE_CONFIG.in_queue_locked_views(view);
213 self.in_queue.push(exit_view, votes, update_views);
214 }
215 }
216
217 pub(super) fn new_unlock(
218 &mut self, view: View, to_unlock_votes: u64,
219 update_views: &mut Vec<View>,
220 ) {
221 if to_unlock_votes == 0 {
222 return;
223 }
224
225 let before_available_votes = self.available_votes;
226 let mut rest_votes = to_unlock_votes;
227
228 let votes = rest_votes.min(self.locked);
230 if votes > 0 {
231 rest_votes -= votes;
232 self.locked -= votes;
233 self.available_votes -= votes;
234
235 let exit_view =
236 view + POS_STATE_CONFIG.out_queue_locked_views(view);
237 self.out_queue.push(exit_view, votes, update_views);
238 }
239
240 while rest_votes > 0 {
242 let maybe_item = self.in_queue.pull(rest_votes);
243
244 if maybe_item.is_none() {
245 diem_warn!(
246 "Not enough votes to unlock: before available votes {}, to unlock votes {}, rest votes {}.",
247 before_available_votes,
248 to_unlock_votes,
249 rest_votes
250 );
251 break;
252 }
253
254 let item = maybe_item.unwrap();
255
256 rest_votes -= item.votes;
257 self.available_votes -= item.votes;
258
259 let exit_view =
260 item.view + POS_STATE_CONFIG.out_queue_locked_views(view);
261 self.out_queue.push(exit_view, item.votes, update_views);
262 }
263 }
264
265 pub(super) fn force_retire(
266 &mut self, view: View, callback_views: &mut Vec<View>,
267 ) {
268 if self.force_retired.is_none() {
269 self.force_retired = Some(view);
270 callback_views
271 .push(view + POS_STATE_CONFIG.force_retired_locked_views(view));
272 self.new_unlock(view, self.available_votes, callback_views);
273 }
274 }
275
276 pub(super) fn forfeit(
277 &mut self, rule: ForfeitRule, updated_views: &mut Vec<View>,
278 ) {
279 if self.exempt_from_forfeit.is_some() {
280 return;
281 }
282 match rule {
283 ForfeitRule::FreezeWithdrawable => {
284 self.exempt_from_forfeit = Some(self.unlocked)
285 }
286 ForfeitRule::RelockOnActive { deadline } => {
287 if self.available_votes > 0 {
291 let mut to_lock_votes = self.available_votes;
292 self.in_queue.clear();
293 self.locked = 0;
294 self.available_votes = 0;
295
296 while let Some(item) = self.out_queue.pop_by_view(u64::MAX)
297 {
298 to_lock_votes += item.votes;
299 }
300 self.force_retired = None;
301
302 self.out_queue.push(deadline, to_lock_votes, updated_views);
303 }
304 }
305 ForfeitRule::RelockAll { deadline } => {
306 self.relock_all(deadline, updated_views)
307 }
308 }
309 }
310
311 fn relock_all(&mut self, deadline: View, updated_views: &mut Vec<View>) {
315 let mut held = Vec::with_capacity(self.out_queue.len() + 1);
319 if self.available_votes > 0 {
320 held.push(StatusItem {
321 view: deadline,
322 votes: self.available_votes,
323 });
324 }
325 while let Some(item) = self.out_queue.pop_by_view(u64::MAX) {
326 held.push(StatusItem {
327 view: item.view.max(deadline),
328 votes: item.votes,
329 });
330 }
331
332 self.in_queue.clear();
335 self.locked = 0;
336 self.available_votes = 0;
337
338 for item in held {
339 self.out_queue.push(item.view, item.votes, updated_views);
340 }
341 }
342}
343
344pub(super) enum ForfeitRule {
346 FreezeWithdrawable,
348 RelockOnActive { deadline: View },
350 RelockAll { deadline: View },
352}
353
354#[cfg(test)]
355mod relock_tests {
356 use super::*;
357
358 fn status_with(out_queue: &[(View, u64)], active: u64) -> NodeLockStatus {
360 let mut status = NodeLockStatus::default();
361 status.available_votes = active;
362 status.locked = active;
363 for (view, votes) in out_queue {
364 status.out_queue.push(*view, *votes, &mut Vec::new());
365 }
366 status
367 }
368
369 #[test]
370 fn relocking_orders_the_queue_and_never_brings_an_exit_forward() {
371 let mut status = status_with(&[(900, 3), (100, 1), (500, 2)], 10);
373 status.force_retired = Some(7);
374 assert!(!status.out_queue.sorted);
375
376 let mut updated = Vec::new();
377 status.forfeit(ForfeitRule::RelockAll { deadline: 500 }, &mut updated);
378
379 let exits: Vec<(View, u64)> = status
380 .out_queue
381 .iter()
382 .map(|item| (item.view, item.votes))
383 .collect();
384 assert_eq!(exits, vec![(500, 10), (500, 1), (500, 2), (900, 3)]);
386 assert!(status.out_queue.sorted);
387 assert_eq!(status.available_votes, 0);
388 assert!(status.in_queue.is_empty());
389 assert_eq!(status.force_retired, Some(7));
390 assert_eq!(updated, vec![500, 500, 500, 900]);
391 }
392}
393
394#[allow(dead_code)]
395pub mod tests {
396 use super::*;
397 use std::collections::HashSet;
398
399 enum Operation {
400 NewLock(u64),
401 NewUnlock(u64),
402 ForceRetire,
403 AssertAvailable(u64),
404 AssertLocked(u64),
405 AssertUnlocked(u64),
406 }
407
408 use Operation::*;
409
410 fn run_tasks(tasks: Vec<(Operation, View)>) {
411 let mut tasks: VecDeque<(Operation, View)> = tasks.into();
412
413 let mut lock_status = NodeLockStatus::default();
414 let mut hint_views = HashSet::<View>::new();
415 let mut view = 0;
416
417 while !(tasks.is_empty() && hint_views.is_empty()) {
418 if hint_views.contains(&view) {
419 lock_status.update(view);
420 hint_views.remove(&view);
421 }
422
423 let mut update_views = Vec::new();
424
425 while tasks.front().map(|x| x.1) == Some(view) {
426 match tasks.pop_front().unwrap().0 {
427 Operation::NewLock(votes) => {
428 lock_status.new_lock(
429 view,
430 votes,
431 false,
432 None,
433 &mut update_views,
434 );
435 }
436 Operation::NewUnlock(votes) => {
437 lock_status.new_unlock(view, votes, &mut update_views);
438 }
439 Operation::ForceRetire => {
440 lock_status.force_retire(view, &mut update_views);
441 }
442 Operation::AssertAvailable(votes) => {
443 if lock_status.available_votes != votes {
444 panic!("View {}\n {:?}", view, lock_status);
445 }
446 }
447 Operation::AssertLocked(votes) => {
448 if lock_status.locked != votes {
449 panic!("View {}\n {:?}", view, lock_status);
450 }
451 }
452 Operation::AssertUnlocked(votes) => {
453 if lock_status.unlocked_votes() != votes {
454 panic!("View {}\n {:?}", view, lock_status);
455 }
456 }
457 }
458 }
459
460 for update_view in update_views {
461 if update_view > view {
462 hint_views.insert(update_view);
463 }
464 }
465 view += 1;
466 }
467 }
468
469 fn basic() {
471 let one_vote = vec![
472 (NewLock(1), 2),
473 (AssertAvailable(1), 3),
474 (AssertLocked(1), 10082),
475 (NewUnlock(1), 20000),
476 (AssertAvailable(0), 20001),
477 (AssertUnlocked(0), 20002),
478 (AssertUnlocked(1), 30080),
479 ];
480
481 let multi_vote = vec![
482 (NewLock(10), 2u64),
483 (AssertAvailable(10), 3),
484 (AssertLocked(10), 10082),
485 (NewUnlock(7), 20000),
486 (AssertAvailable(3), 20001),
487 (AssertUnlocked(0), 20002),
488 (AssertUnlocked(7), 30080),
489 (AssertAvailable(3), 30081),
490 ];
491
492 run_tasks(one_vote);
493 run_tasks(multi_vote);
494 }
495
496 fn increase_during_exit() {
498 let tasks = vec![
499 (NewLock(10), 2),
500 (AssertAvailable(10), 3),
501 (NewLock(5), 4),
502 (AssertAvailable(15), 5),
503 (NewUnlock(7), 20000),
504 (AssertAvailable(8), 20001),
505 (NewLock(5), 20002),
506 (AssertAvailable(13), 20003),
507 (NewUnlock(7), 20004),
508 (AssertAvailable(6), 20005),
509 (AssertUnlocked(0), 20005),
510 (NewUnlock(3), 20006),
511 (AssertUnlocked(7), 30080),
512 (AssertUnlocked(14), 30084),
513 (AssertUnlocked(15), 30086),
514 (AssertUnlocked(15), 40161),
515 (AssertUnlocked(17), 40162),
516 ];
517
518 run_tasks(tasks);
519 }
520
521 fn force_retire() {
523 let tasks = vec![
524 (NewLock(6), 2),
525 (AssertAvailable(6), 3),
526 (NewLock(7), 12),
527 (AssertAvailable(13), 13),
528 (AssertLocked(6), 10090),
529 (ForceRetire, 10090),
530 (NewLock(8), 10092),
531 (AssertAvailable(0), 10093),
532 (AssertLocked(0), 10093),
533 (AssertUnlocked(0), 20169),
534 (AssertUnlocked(6), 20170),
535 (NewLock(9), 20170),
536 (AssertAvailable(9), 20171),
537 (AssertUnlocked(13), 20172),
538 (AssertLocked(0), 30249),
539 (AssertLocked(9), 30250),
540 (AssertUnlocked(13), 30251),
541 (AssertUnlocked(21), 30252),
542 ];
543
544 run_tasks(tasks);
545 }
546
547 fn resolve_retired() {
548 let tasks = vec![
549 (NewLock(6), 2),
550 (AssertAvailable(6), 3),
551 (ForceRetire, 10),
552 (NewLock(8), 10090),
553 (AssertAvailable(8), 10091),
554 ];
555
556 run_tasks(tasks);
557 }
558
559 pub fn run_all() {
560 basic();
561 increase_during_exit();
562 force_retire();
563 resolve_retired();
564 }
565}