committee.rs
1 // Copyright (c) 2019-2025 Alpha-Delta Network Inc. 2 // This file is part of the deltavm library. 3 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at: 7 8 // http://www.apache.org/licenses/LICENSE-2.0 9 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 use crate::{ 17 atomic_batch_scope, 18 helpers::{Map, MapRead}, 19 }; 20 use deltavm_ledger_committee::Committee; 21 use console::network::prelude::*; 22 23 use alphastd_storage::StorageMode; 24 use anyhow::Result; 25 use core::marker::PhantomData; 26 27 const ROUND_KEY: u8 = 0; 28 29 /// A trait for committee storage. 30 pub trait CommitteeStorage<N: Network>: 'static + Clone + Send + Sync { 31 /// The mapping of `()` to `current round`. 32 type CurrentRoundMap: for<'a> Map<'a, u8, u64>; 33 /// The mapping of `round` to `height`. 34 type RoundToHeightMap: for<'a> Map<'a, u64, u32>; 35 /// The mapping of `block height` to `committee`. 36 type CommitteeMap: for<'a> Map<'a, u32, Committee<N>>; 37 38 /// Initializes the committee storage. 39 fn open<S: Into<StorageMode>>(storage: S) -> Result<Self>; 40 41 /// Returns the current round map. 42 fn current_round_map(&self) -> &Self::CurrentRoundMap; 43 /// Returns the round to height map. 44 fn round_to_height_map(&self) -> &Self::RoundToHeightMap; 45 /// Returns the committee map. 46 fn committee_map(&self) -> &Self::CommitteeMap; 47 48 /// Returns the storage mode. 49 fn storage_mode(&self) -> &StorageMode; 50 51 /// Starts an atomic batch write operation. 52 fn start_atomic(&self) { 53 self.current_round_map().start_atomic(); 54 self.round_to_height_map().start_atomic(); 55 self.committee_map().start_atomic(); 56 } 57 58 /// Checks if an atomic batch is in progress. 59 fn is_atomic_in_progress(&self) -> bool { 60 self.current_round_map().is_atomic_in_progress() 61 || self.round_to_height_map().is_atomic_in_progress() 62 || self.committee_map().is_atomic_in_progress() 63 } 64 65 /// Checkpoints the atomic batch. 66 fn atomic_checkpoint(&self) { 67 self.current_round_map().atomic_checkpoint(); 68 self.round_to_height_map().atomic_checkpoint(); 69 self.committee_map().atomic_checkpoint(); 70 } 71 72 /// Clears the latest atomic batch checkpoint. 73 fn clear_latest_checkpoint(&self) { 74 self.current_round_map().clear_latest_checkpoint(); 75 self.round_to_height_map().clear_latest_checkpoint(); 76 self.committee_map().clear_latest_checkpoint(); 77 } 78 79 /// Rewinds the atomic batch to the previous checkpoint. 80 fn atomic_rewind(&self) { 81 self.current_round_map().atomic_rewind(); 82 self.round_to_height_map().atomic_rewind(); 83 self.committee_map().atomic_rewind(); 84 } 85 86 /// Aborts an atomic batch write operation. 87 fn abort_atomic(&self) { 88 self.current_round_map().abort_atomic(); 89 self.round_to_height_map().abort_atomic(); 90 self.committee_map().abort_atomic(); 91 } 92 93 /// Finishes an atomic batch write operation. 94 fn finish_atomic(&self) -> Result<()> { 95 self.current_round_map().finish_atomic()?; 96 self.round_to_height_map().finish_atomic()?; 97 self.committee_map().finish_atomic() 98 } 99 100 /// Stores the given `(next height, committee)` pair into storage, 101 /// and indexes storage up to the `next round`. 102 fn insert(&self, next_height: u32, committee: Committee<N>) -> Result<()> { 103 // Retrieve the next round. 104 let next_round = committee.starting_round(); 105 // Ensure the next round is at least the next height. 106 ensure!(next_round >= next_height as u64, "Next round must be at least the next height"); 107 108 // Check the next round. 109 match self.current_round() { 110 // If the current round is 0, ensure the next round is 0. 111 Err(..) => ensure!(next_round == 0, "Next round must be block round 0"), 112 // Otherwise, ensure the next round sequentially follows the current round. 113 Ok(current_round) => ensure!( 114 next_round > current_round, 115 "Next round {next_round} must be greater than current round {current_round}" 116 ), 117 } 118 119 // Check the next height. 120 match self.current_height() { 121 // If the current height is 0, ensure the next height is 0. 122 Err(..) => ensure!(next_height == 0, "Next height must be block height 0"), 123 // Otherwise, ensure the next height sequentially follows the current height. 124 Ok(current_height) => ensure!(next_height == current_height + 1, "Next height must be sequential"), 125 } 126 127 // If the next round already exists, then return an error. 128 ensure!( 129 !self.round_to_height_map().contains_key_confirmed(&next_round)?, 130 "Next round {next_round} already exists in committee storage" 131 ); 132 133 // Determine the catch up round. 134 let catch_up_round = match self.current_round() { 135 Err(..) => 0, 136 Ok(current_round) => current_round + 1, 137 }; 138 139 // Start an atomic batch. 140 atomic_batch_scope!(self, { 141 // Store the next round. 142 self.current_round_map().insert(ROUND_KEY, next_round)?; 143 144 // If the current height exists, then store missing rounds up to the *next* height. 145 if let Ok(current_height) = self.current_height() { 146 // Store the round to height mappings. 147 for round in catch_up_round..next_round { 148 // Note: We store the 'current_height' as the *next* round starts the *next* height. 149 self.round_to_height_map().insert(round, current_height)?; 150 } 151 } 152 // Store the next round's height. 153 self.round_to_height_map().insert(next_round, next_height)?; 154 155 // Store the committee. 156 self.committee_map().insert(next_height, committee)?; 157 Ok(()) 158 }) 159 } 160 161 /// Removes the committee for the given `height`, in the process 162 /// removing all round to height entries back to the previous committee. 163 fn remove(&self, height: u32) -> Result<()> { 164 // Retrieve the current round. 165 let current_round = self.current_round()?; 166 // Retrieve the current height. 167 let current_height = self.current_height()?; 168 // Retrieve the committee for the given height. 169 let Some(committee) = self.get_committee(height)? else { 170 bail!("Committee not found for height {height} in committee storage"); 171 }; 172 // Retrieve the round for the given height. 173 let committee_round = committee.starting_round(); 174 175 // If the current height matches the given height, it means we are removing the latest committee, 176 // and we will need to update the current round. 177 let is_latest_committee = current_height == height; 178 179 // Find the earliest round to be removed (inclusive). 180 let mut earliest_round = committee_round; 181 while earliest_round > 0 && self.get_height_for_round(earliest_round)? == Some(height) { 182 earliest_round = earliest_round.saturating_sub(1); 183 } 184 let is_multiple = earliest_round != committee_round; 185 if is_multiple { 186 earliest_round += 1; 187 } 188 189 // Find the latest round to be removed (exclusive). 190 let mut latest_round = committee_round; 191 while self.get_height_for_round(latest_round)? == Some(height) { 192 latest_round = latest_round.saturating_add(1); 193 } 194 195 // If we are removing the latest committee, determine the next current round. 196 let mut next_current_round = current_round.saturating_sub(1); 197 if is_latest_committee { 198 while next_current_round > 0 { 199 // If the next current height is less than the current height, then we have found the next current round. 200 if let Some(next_current_height) = self.get_height_for_round(next_current_round)? { 201 if next_current_height < current_height { 202 break; 203 } 204 } 205 // Otherwise, decrement the next current round. 206 next_current_round = next_current_round.saturating_sub(1); 207 } 208 } 209 210 // Start an atomic batch. 211 atomic_batch_scope!(self, { 212 // Update the current round, if this is the latest round. 213 if is_latest_committee { 214 // Remove the current round. 215 self.current_round_map().remove(&ROUND_KEY)?; 216 // If the next current round is greater than 0, then insert it. 217 if current_round != next_current_round && next_current_round > 0 { 218 // Insert the previous round. 219 self.current_round_map().insert(ROUND_KEY, next_current_round)?; 220 } 221 } 222 // Remove the round to height mappings. 223 for round in earliest_round..latest_round { 224 self.round_to_height_map().remove(&round)?; 225 } 226 // Remove the committee. 227 self.committee_map().remove(&height)?; 228 229 Ok(()) 230 }) 231 } 232 233 /// Returns the current round. 234 fn current_round(&self) -> Result<u64> { 235 let Some(round) = self.current_round_map().get_confirmed(&ROUND_KEY)? else { 236 bail!("Current round not found in committee storage"); 237 }; 238 Ok(*round) 239 } 240 241 /// Returns the current height. 242 fn current_height(&self) -> Result<u32> { 243 // Retrieve the current round. 244 let current_round = self.current_round()?; 245 // Retrieve the current height. 246 let Some(height) = self.round_to_height_map().get_confirmed(¤t_round)? else { 247 bail!("Current height not found in committee storage"); 248 }; 249 Ok(*height) 250 } 251 252 /// Returns the current committee. 253 fn current_committee(&self) -> Result<Committee<N>> { 254 self.get_committee(self.current_height()?)? 255 .ok_or_else(|| anyhow!("Current committee not found in committee storage")) 256 } 257 258 /// Returns the height for the given `round`. 259 fn get_height_for_round(&self, round: u64) -> Result<Option<u32>> { 260 Ok(self.round_to_height_map().get_confirmed(&round)?.map(|x| *x)) 261 } 262 263 /// Returns the committee for the given `height`. 264 fn get_committee(&self, height: u32) -> Result<Option<Committee<N>>> { 265 Ok(self.committee_map().get_confirmed(&height)?.map(|x| x.into_owned())) 266 } 267 268 /// Returns the committee for the given `round`. 269 fn get_committee_for_round(&self, round: u64) -> Result<Option<Committee<N>>> { 270 // Retrieve the height for the given round. 271 match self.get_height_for_round(round)? { 272 // Retrieve the committee for the given height. 273 Some(height) => Ok(self.get_committee(height)?), 274 None => Ok(None), 275 } 276 } 277 } 278 279 /// The committee store. 280 #[derive(Clone)] 281 pub struct CommitteeStore<N: Network, C: CommitteeStorage<N>> { 282 /// The committee storage. 283 storage: C, 284 /// PhantomData. 285 _phantom: PhantomData<N>, 286 } 287 288 impl<N: Network, C: CommitteeStorage<N>> CommitteeStore<N, C> { 289 /// Initializes the committee store. 290 pub fn open<S: Into<StorageMode>>(storage: S) -> Result<Self> { 291 // Initialize the committee storage. 292 let storage = C::open(storage)?; 293 // Return the committee store. 294 Ok(Self { storage, _phantom: PhantomData }) 295 } 296 297 /// Initializes a committee store from storage. 298 pub fn from(storage: C) -> Self { 299 Self { storage, _phantom: PhantomData } 300 } 301 302 /// Starts an atomic batch write operation. 303 pub fn start_atomic(&self) { 304 self.storage.start_atomic(); 305 } 306 307 /// Checks if an atomic batch is in progress. 308 pub fn is_atomic_in_progress(&self) -> bool { 309 self.storage.is_atomic_in_progress() 310 } 311 312 /// Checkpoints the atomic batch. 313 pub fn atomic_checkpoint(&self) { 314 self.storage.atomic_checkpoint(); 315 } 316 317 /// Clears the latest atomic batch checkpoint. 318 pub fn clear_latest_checkpoint(&self) { 319 self.storage.clear_latest_checkpoint(); 320 } 321 322 /// Rewinds the atomic batch to the previous checkpoint. 323 pub fn atomic_rewind(&self) { 324 self.storage.atomic_rewind(); 325 } 326 327 /// Aborts an atomic batch write operation. 328 pub fn abort_atomic(&self) { 329 self.storage.abort_atomic(); 330 } 331 332 /// Finishes an atomic batch write operation. 333 pub fn finish_atomic(&self) -> Result<()> { 334 self.storage.finish_atomic() 335 } 336 337 /// Returns the storage mode. 338 pub fn storage_mode(&self) -> &StorageMode { 339 self.storage.storage_mode() 340 } 341 } 342 343 impl<N: Network, C: CommitteeStorage<N>> CommitteeStore<N, C> { 344 /// Stores the given `(next height, committee)` pair into storage, 345 /// and indexes storage up to the `next round`. 346 pub fn insert(&self, next_height: u32, committee: Committee<N>) -> Result<()> { 347 self.storage.insert(next_height, committee) 348 } 349 350 /// Removes the committee for the given `height`, in the process 351 /// removing all round to height entries back to the previous committee. 352 pub fn remove(&self, height: u32) -> Result<()> { 353 self.storage.remove(height) 354 } 355 } 356 357 impl<N: Network, C: CommitteeStorage<N>> CommitteeStore<N, C> { 358 /// Returns the current round. 359 pub fn current_round(&self) -> Result<u64> { 360 self.storage.current_round() 361 } 362 363 /// Returns the current height. 364 pub fn current_height(&self) -> Result<u32> { 365 self.storage.current_height() 366 } 367 368 /// Returns the current committee. 369 pub fn current_committee(&self) -> Result<Committee<N>> { 370 self.storage.current_committee() 371 } 372 373 /// Returns the height for the given `round`. 374 pub fn get_height_for_round(&self, round: u64) -> Result<Option<u32>> { 375 self.storage.get_height_for_round(round) 376 } 377 378 /// Returns the committee for the given `height`. 379 pub fn get_committee(&self, height: u32) -> Result<Option<Committee<N>>> { 380 self.storage.get_committee(height) 381 } 382 383 /// Returns the committee for the given `round`. 384 pub fn get_committee_for_round(&self, round: u64) -> Result<Option<Committee<N>>> { 385 self.storage.get_committee_for_round(round) 386 } 387 } 388 389 #[cfg(test)] 390 mod tests { 391 use super::*; 392 use crate::helpers::memory::CommitteeMemory; 393 394 type CurrentNetwork = console::network::MainnetV0; 395 396 #[test] 397 fn test_insert_get_remove() { 398 let rng = &mut TestRng::default(); 399 400 // Sample the committee. 401 let committee_0 = deltavm_ledger_committee::test_helpers::sample_committee_for_round(0, rng); 402 403 // Initialize a new committee store. 404 let store = CommitteeStore::<CurrentNetwork, CommitteeMemory<_>>::open(StorageMode::new_test(None)).unwrap(); 405 assert!(store.current_round().is_err()); 406 assert!(store.current_height().is_err()); 407 assert!(store.current_committee().is_err()); 408 assert_eq!(store.get_height_for_round(rng.r#gen()).unwrap(), None); 409 assert_eq!(store.get_committee(rng.r#gen()).unwrap(), None); 410 assert_eq!(store.get_committee_for_round(rng.r#gen()).unwrap(), None); 411 412 // Insert the committee. 413 store.insert(0, committee_0.clone()).unwrap(); 414 assert_eq!(store.current_round().unwrap(), 0); 415 assert_eq!(store.current_height().unwrap(), 0); 416 assert_eq!(store.current_committee().unwrap(), committee_0); 417 418 assert_eq!(store.get_height_for_round(0).unwrap().unwrap(), 0); 419 assert_eq!(store.get_height_for_round(1).unwrap(), None); 420 assert_eq!(store.get_height_for_round(2).unwrap(), None); 421 assert_eq!(store.get_height_for_round(3).unwrap(), None); 422 423 assert_eq!(store.get_committee(0).unwrap().unwrap(), committee_0); 424 assert_eq!(store.get_committee(1).unwrap(), None); 425 assert_eq!(store.get_committee(2).unwrap(), None); 426 427 assert_eq!(store.get_committee_for_round(0).unwrap().unwrap(), committee_0); 428 assert_eq!(store.get_committee_for_round(1).unwrap(), None); 429 assert_eq!(store.get_committee_for_round(2).unwrap(), None); 430 assert_eq!(store.get_committee_for_round(3).unwrap(), None); 431 432 // Sample another committee. 433 let committee_1 = deltavm_ledger_committee::test_helpers::sample_committee_for_round(5, rng); 434 435 // Insert the committee. 436 store.insert(1, committee_1.clone()).unwrap(); 437 assert_eq!(store.current_round().unwrap(), 5); 438 assert_eq!(store.current_height().unwrap(), 1); 439 assert_eq!(store.current_committee().unwrap(), committee_1); 440 441 assert_eq!(store.get_height_for_round(0).unwrap().unwrap(), 0); 442 assert_eq!(store.get_height_for_round(1).unwrap().unwrap(), 0); 443 assert_eq!(store.get_height_for_round(2).unwrap().unwrap(), 0); 444 assert_eq!(store.get_height_for_round(3).unwrap().unwrap(), 0); 445 assert_eq!(store.get_height_for_round(4).unwrap().unwrap(), 0); 446 assert_eq!(store.get_height_for_round(5).unwrap().unwrap(), 1); 447 assert_eq!(store.get_height_for_round(6).unwrap(), None); 448 449 assert_eq!(store.get_committee(0).unwrap().unwrap(), committee_0); 450 assert_eq!(store.get_committee(1).unwrap().unwrap(), committee_1); 451 assert_eq!(store.get_committee(2).unwrap(), None); 452 assert_eq!(store.get_committee(3).unwrap(), None); 453 454 assert_eq!(store.get_committee_for_round(0).unwrap().unwrap(), committee_0); 455 assert_eq!(store.get_committee_for_round(1).unwrap().unwrap(), committee_0); 456 assert_eq!(store.get_committee_for_round(2).unwrap().unwrap(), committee_0); 457 assert_eq!(store.get_committee_for_round(3).unwrap().unwrap(), committee_0); 458 assert_eq!(store.get_committee_for_round(4).unwrap().unwrap(), committee_0); 459 assert_eq!(store.get_committee_for_round(5).unwrap().unwrap(), committee_1); 460 assert_eq!(store.get_committee_for_round(6).unwrap(), None); 461 462 // Remove the committee. 463 assert!(store.remove(2).is_err()); 464 store.remove(1).unwrap(); 465 assert!(store.remove(1).is_err()); 466 assert_eq!(store.current_round().unwrap(), 4); 467 assert_eq!(store.current_height().unwrap(), 0); 468 assert_eq!(store.current_committee().unwrap(), committee_0); 469 470 assert_eq!(store.get_height_for_round(0).unwrap().unwrap(), 0); 471 assert_eq!(store.get_height_for_round(1).unwrap().unwrap(), 0); 472 assert_eq!(store.get_height_for_round(2).unwrap().unwrap(), 0); 473 assert_eq!(store.get_height_for_round(3).unwrap().unwrap(), 0); 474 assert_eq!(store.get_height_for_round(4).unwrap().unwrap(), 0); 475 assert_eq!(store.get_height_for_round(5).unwrap(), None); 476 assert_eq!(store.get_height_for_round(6).unwrap(), None); 477 478 assert_eq!(store.get_committee(0).unwrap().unwrap(), committee_0); 479 assert_eq!(store.get_committee(1).unwrap(), None); 480 assert_eq!(store.get_committee(2).unwrap(), None); 481 assert_eq!(store.get_committee(3).unwrap(), None); 482 483 assert_eq!(store.get_committee_for_round(0).unwrap().unwrap(), committee_0); 484 assert_eq!(store.get_committee_for_round(1).unwrap().unwrap(), committee_0); 485 assert_eq!(store.get_committee_for_round(2).unwrap().unwrap(), committee_0); 486 assert_eq!(store.get_committee_for_round(3).unwrap().unwrap(), committee_0); 487 assert_eq!(store.get_committee_for_round(4).unwrap().unwrap(), committee_0); 488 assert_eq!(store.get_committee_for_round(5).unwrap(), None); 489 assert_eq!(store.get_committee_for_round(6).unwrap(), None); 490 491 // Remove the committee. 492 store.remove(0).unwrap(); 493 assert!(store.current_round().is_err()); 494 assert!(store.current_height().is_err()); 495 assert!(store.current_committee().is_err()); 496 497 assert_eq!(store.get_height_for_round(0).unwrap(), None); 498 assert_eq!(store.get_height_for_round(1).unwrap(), None); 499 assert_eq!(store.get_height_for_round(2).unwrap(), None); 500 assert_eq!(store.get_height_for_round(3).unwrap(), None); 501 assert_eq!(store.get_height_for_round(4).unwrap(), None); 502 assert_eq!(store.get_height_for_round(5).unwrap(), None); 503 504 assert_eq!(store.get_committee(0).unwrap(), None); 505 assert_eq!(store.get_committee(1).unwrap(), None); 506 assert_eq!(store.get_committee(2).unwrap(), None); 507 assert_eq!(store.get_committee(3).unwrap(), None); 508 509 assert_eq!(store.get_committee_for_round(0).unwrap(), None); 510 assert_eq!(store.get_committee_for_round(1).unwrap(), None); 511 assert_eq!(store.get_committee_for_round(2).unwrap(), None); 512 assert_eq!(store.get_committee_for_round(3).unwrap(), None); 513 assert_eq!(store.get_committee_for_round(4).unwrap(), None); 514 assert_eq!(store.get_committee_for_round(5).unwrap(), None); 515 } 516 517 #[test] 518 fn test_remove_hole() { 519 let rng = &mut TestRng::default(); 520 521 // Sample the committee. 522 let committee_0 = deltavm_ledger_committee::test_helpers::sample_committee_for_round(0, rng); 523 524 // Initialize a new committee store. 525 let store = CommitteeStore::<CurrentNetwork, CommitteeMemory<_>>::open(StorageMode::new_test(None)).unwrap(); 526 assert!(store.current_round().is_err()); 527 assert!(store.current_height().is_err()); 528 assert!(store.current_committee().is_err()); 529 530 // Insert the committee. 531 store.insert(0, committee_0.clone()).unwrap(); 532 assert_eq!(store.current_round().unwrap(), 0); 533 assert_eq!(store.current_height().unwrap(), 0); 534 assert_eq!(store.current_committee().unwrap(), committee_0); 535 536 // Sample another committee. 537 let committee_1 = deltavm_ledger_committee::test_helpers::sample_committee_for_round(5, rng); 538 539 // Insert the committee. 540 store.insert(1, committee_1.clone()).unwrap(); 541 assert_eq!(store.current_round().unwrap(), 5); 542 assert_eq!(store.current_height().unwrap(), 1); 543 assert_eq!(store.current_committee().unwrap(), committee_1); 544 545 // Observe: We remove the committee for round 2, but the current committee is still the one for round 5. 546 547 // Remove the committee. 548 store.remove(0).unwrap(); 549 assert_eq!(store.current_round().unwrap(), 5); 550 assert_eq!(store.current_height().unwrap(), 1); 551 assert_eq!(store.current_committee().unwrap(), committee_1); 552 553 assert_eq!(store.get_height_for_round(1).unwrap(), None); 554 assert_eq!(store.get_height_for_round(2).unwrap(), None); 555 assert_eq!(store.get_height_for_round(3).unwrap(), None); 556 assert_eq!(store.get_height_for_round(4).unwrap(), None); 557 assert_eq!(store.get_height_for_round(5).unwrap().unwrap(), 1); 558 assert_eq!(store.get_height_for_round(6).unwrap(), None); 559 560 assert_eq!(store.get_committee(0).unwrap(), None); 561 assert_eq!(store.get_committee(1).unwrap().unwrap(), committee_1); 562 assert_eq!(store.get_committee(2).unwrap(), None); 563 assert_eq!(store.get_committee(3).unwrap(), None); 564 565 assert_eq!(store.get_committee_for_round(1).unwrap(), None); 566 assert_eq!(store.get_committee_for_round(2).unwrap(), None); 567 assert_eq!(store.get_committee_for_round(3).unwrap(), None); 568 assert_eq!(store.get_committee_for_round(4).unwrap(), None); 569 assert_eq!(store.get_committee_for_round(5).unwrap().unwrap(), committee_1); 570 assert_eq!(store.get_committee_for_round(6).unwrap(), None); 571 572 // Remove the committee. 573 store.remove(1).unwrap(); 574 assert!(store.current_round().is_err()); 575 assert!(store.current_height().is_err()); 576 assert!(store.current_committee().is_err()); 577 578 assert_eq!(store.get_height_for_round(1).unwrap(), None); 579 assert_eq!(store.get_height_for_round(2).unwrap(), None); 580 assert_eq!(store.get_height_for_round(3).unwrap(), None); 581 assert_eq!(store.get_height_for_round(4).unwrap(), None); 582 assert_eq!(store.get_height_for_round(5).unwrap(), None); 583 584 assert_eq!(store.get_committee(0).unwrap(), None); 585 assert_eq!(store.get_committee(1).unwrap(), None); 586 assert_eq!(store.get_committee(2).unwrap(), None); 587 assert_eq!(store.get_committee(3).unwrap(), None); 588 589 assert_eq!(store.get_committee_for_round(1).unwrap(), None); 590 assert_eq!(store.get_committee_for_round(2).unwrap(), None); 591 assert_eq!(store.get_committee_for_round(3).unwrap(), None); 592 assert_eq!(store.get_committee_for_round(4).unwrap(), None); 593 assert_eq!(store.get_committee_for_round(5).unwrap(), None); 594 } 595 }