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 }