/ ledger / tests / pending_blocks.rs
pending_blocks.rs
  1  // Copyright (c) 2025 ADnet Contributors
  2  // This file is part of the AlphaVM 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  mod helpers;
 17  use helpers::{BlockOptions, CurrentNetwork, LedgerType, TestChainBuilder};
 18  
 19  use alphavm_ledger::{CheckBlockError, Ledger};
 20  
 21  use alpha_std::StorageMode;
 22  use alphavm_utilities::TestRng;
 23  
 24  #[test]
 25  fn test_preprocess_block() {
 26      let rng = &mut TestRng::default();
 27  
 28      let mut builder = TestChainBuilder::new(4, rng);
 29  
 30      // Construct the ledger.
 31      let ledger = Ledger::<CurrentNetwork, LedgerType<CurrentNetwork>>::load(
 32          builder.genesis_block().clone(),
 33          StorageMode::new_test(None),
 34      )
 35      .unwrap();
 36  
 37      // Generate a bunch of blocks that do not contain votes
 38      let mut pending_blocks = vec![];
 39  
 40      for block in builder.generate_blocks_with_opts(5, &BlockOptions { skip_votes: true, ..Default::default() }, rng) {
 41          if !pending_blocks.is_empty() {
 42              // We shoud only be able to pre-process a pending block if the previous
 43              // blocks are applied to the ledger or in the pending set
 44              assert!(ledger.check_block_subdag(block.clone(), &[]).is_err());
 45          }
 46  
 47          let pending_block = ledger.check_block_subdag(block, &pending_blocks).unwrap();
 48          pending_blocks.push(pending_block);
 49      }
 50  
 51      // Now, create a "vote block" that contains sufficient votes to the previous leader block
 52      let vote_block = builder.generate_block(rng);
 53      assert!(ledger.check_next_block(&vote_block, rng).is_err());
 54  
 55      for pending in pending_blocks.into_iter() {
 56          let block = ledger.check_block_content(pending, rng).expect("Pending block should be accepted");
 57          assert!(ledger.advance_to_next_block(&block).is_ok());
 58      }
 59  
 60      // Now the commit block should be accepted
 61      assert!(ledger.check_next_block(&vote_block, rng).is_ok());
 62      assert!(ledger.advance_to_next_block(&vote_block).is_ok());
 63  }
 64  
 65  #[test]
 66  fn test_check_block_error_display() {
 67      // Test that CheckBlockError implements Display correctly
 68      let error = CheckBlockError::<CurrentNetwork>::InvalidHash;
 69      let display_string = format!("{error}");
 70      assert_eq!(display_string, "Block has invalid hash");
 71  
 72      let error = CheckBlockError::<CurrentNetwork>::InvalidHeight { expected: 5, actual: 3 };
 73      let display_string = format!("{error}");
 74      assert!(display_string.contains("Expected 5"));
 75      assert!(display_string.contains("got 3"));
 76  }
 77  
 78  #[test]
 79  fn test_prefix_with_duplicate_block_error() {
 80      let rng = &mut TestRng::default();
 81      let mut builder = TestChainBuilder::new(4, rng);
 82  
 83      // Construct the ledger.
 84      let ledger = Ledger::<CurrentNetwork, LedgerType<CurrentNetwork>>::load(
 85          builder.genesis_block().clone(),
 86          StorageMode::new_test(None),
 87      )
 88      .unwrap();
 89  
 90      // Generate a block
 91      let block1 = builder.generate_block(rng);
 92  
 93      // Add block1 to ledger
 94      ledger.advance_to_next_block(&block1).unwrap();
 95  
 96      // Generate another block
 97      let block2 = builder.generate_block(rng);
 98  
 99      // So instead, test that we can't check a block with empty prefix when it's not the next block
100  
101      // Generate one more block to skip block2
102      let block3 = builder.generate_block(rng);
103  
104      // Try to check block3 without having block2 in prefix
105      // This will fail with InvalidHeight
106      let result = ledger.check_block_subdag(block3.clone(), &[]);
107      assert!(matches!(result, Err(CheckBlockError::InvalidHeight { expected: 2, actual: 3 })));
108  
109      // Check that the check succeeds when block2 is in the prefix
110      let block2 = ledger.check_block_subdag(block2, &[]).unwrap();
111  
112      // The check should still fail without the prefix.
113      let result = ledger.check_block_subdag(block3.clone(), &[]);
114      assert!(matches!(result, Err(CheckBlockError::InvalidHeight { expected: 2, actual: 3 })));
115  
116      // But succeed with the prefix.
117      let block3 = ledger.check_block_subdag(block3, &[block2.clone()]).unwrap();
118  
119      // Create a forth block
120      let block4 = builder.generate_block(rng);
121  
122      // Test a prefix that contains block2 twice.
123      let result = ledger.check_block_subdag(block4.clone(), &[block2.clone(), block2.clone(), block3.clone()]);
124      assert!(matches!(result, Err(CheckBlockError::InvalidPrefix { index: 1, .. })));
125      let CheckBlockError::InvalidPrefix { error, .. } = result.unwrap_err() else { unreachable!() };
126      assert!(matches!(*error, CheckBlockError::InvalidHeight { expected: 3, actual: 2 }));
127  
128      // Test a prefix that misses block 2.
129      let result = ledger.check_block_subdag(block4.clone(), &[block3]);
130      assert!(matches!(result, Err(CheckBlockError::InvalidPrefix { index: 0, .. })));
131      let CheckBlockError::InvalidPrefix { error, .. } = result.unwrap_err() else { unreachable!() };
132      assert!(matches!(*error, CheckBlockError::InvalidHeight { expected: 2, actual: 3 }));
133  }
134  
135  #[test]
136  fn test_check_block_content_invalid_height() {
137      let rng = &mut TestRng::default();
138      let mut builder = TestChainBuilder::new(4, rng);
139  
140      // Construct the ledger.
141      let ledger = Ledger::<CurrentNetwork, LedgerType<CurrentNetwork>>::load(
142          builder.genesis_block().clone(),
143          StorageMode::new_test(None),
144      )
145      .unwrap();
146  
147      // Generate two blocks
148      let blocks = builder.generate_blocks_with_opts(2, &BlockOptions { skip_votes: true, ..Default::default() }, rng);
149      let block1 = blocks[0].clone();
150  
151      // Check block1 and get pending block
152      let pending1 = ledger.check_block_subdag(block1.clone(), &[]).unwrap();
153  
154      // Advance ledger with block1
155      let verified1 = ledger.check_block_content(pending1.clone(), rng).unwrap();
156      ledger.advance_to_next_block(&verified1).unwrap();
157  
158      // Now try to check_block_content on pending1 again
159      // This should fail because the ledger has already advanced
160      let result = ledger.check_block_content(pending1, rng);
161  
162      assert!(matches!(result, Err(CheckBlockError::InvalidHeight { expected: 2, actual: 1 })));
163  }