Skip to content

Commit 80e5865

Browse files
committed
fix(ledger): tests and fixes for check_block_*
1 parent 81cefc3 commit 80e5865

File tree

2 files changed

+108
-2
lines changed

2 files changed

+108
-2
lines changed

ledger/src/check_next_block.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ impl<N: Network> Deref for PendingBlock<N> {
3434
}
3535
}
3636

37+
impl<N: Network> Debug for PendingBlock<N> {
38+
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
39+
write!(f, "PendingBlock {{ height: {}, hash: {} }}", self.height(), self.hash())
40+
}
41+
}
42+
3743
/// Error returned by [`Self::check_block_subdag`] and [`Self::check_block_subdag_inner`].
3844
///
3945
/// This allows parsing for begning errors, such as the block already existing in the ledger.
@@ -198,7 +204,7 @@ impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
198204
let latest_block = self.current_block.read();
199205

200206
// Ensure, again, that the ledger has not advanced yet. This prevents cryptic errors form appearing during the block check.
201-
if block.height() + 1 != latest_block.height() {
207+
if block.height() != latest_block.height() + 1 {
202208
return Err(CheckBlockError::InvalidHeight { expected: latest_block.height() + 1, actual: block.height() });
203209
}
204210

ledger/tests/pending_blocks.rs

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
mod helpers;
1717
use helpers::{BlockOptions, CurrentNetwork, LedgerType, TestChainBuilder};
1818

19-
use snarkvm_ledger::Ledger;
19+
use snarkvm_ledger::{CheckBlockError, Ledger};
2020

2121
use aleo_std::StorageMode;
2222
use snarkvm_utilities::TestRng;
@@ -61,3 +61,103 @@ fn test_preprocess_block() {
6161
assert!(ledger.check_next_block(&vote_block, rng).is_ok());
6262
assert!(ledger.advance_to_next_block(&vote_block).is_ok());
6363
}
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

Comments
 (0)