|
16 | 16 | mod helpers; |
17 | 17 | use helpers::{BlockOptions, CurrentNetwork, LedgerType, TestChainBuilder}; |
18 | 18 |
|
19 | | -use snarkvm_ledger::Ledger; |
| 19 | +use snarkvm_ledger::{CheckBlockError, Ledger}; |
20 | 20 |
|
21 | 21 | use aleo_std::StorageMode; |
22 | 22 | use snarkvm_utilities::TestRng; |
@@ -61,3 +61,103 @@ fn test_preprocess_block() { |
61 | 61 | assert!(ledger.check_next_block(&vote_block, rng).is_ok()); |
62 | 62 | assert!(ledger.advance_to_next_block(&vote_block).is_ok()); |
63 | 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 { height: 2, .. }))); |
| 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 { height: 3, .. }))); |
| 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 | +} |
0 commit comments