@@ -19,25 +19,112 @@ use crate::narwhal::BatchHeader;
1919
2020use anyhow:: { Context , bail} ;
2121
22+ /// Wrapper for a block that has a valid subDAG, but where the block header,
23+ /// solutions, and transmissions have not been verified yet.
24+ ///
25+ /// This type is created by `Ledger::check_block_subdag` and consumed by `Ledger::check_block_content`.
26+ #[ derive( Clone , PartialEq , Eq ) ]
27+ pub struct PendingBlock < N : Network > ( Block < N > ) ;
28+
29+ impl < N : Network > Deref for PendingBlock < N > {
30+ type Target = Block < N > ;
31+
32+ fn deref ( & self ) -> & Block < N > {
33+ & self . 0
34+ }
35+ }
36+
2237impl < N : Network , C : ConsensusStorage < N > > Ledger < N , C > {
23- /// Checks the given block is valid next block.
38+ /// Checks that the subDAG in a given block is valid, but does not fully verify the block.
2439 ///
25- /// # Panics
26- /// This function panics if called from an async context.
27- pub fn check_next_block < R : CryptoRng + Rng > ( & self , block : & Block < N > , rng : & mut R ) -> Result < ( ) > {
28- let height = block. height ( ) ;
29- let latest_block = self . latest_block ( ) ;
40+ /// # Arguments
41+ /// * `block` - The block to check.
42+ /// * `pending_block` - A sequence of blocks between the block to check and the current height of the ledger.
43+ ///
44+ /// # Notes
45+ /// * This does *not* check that the header of the block is correct or execute/verify any of the transmissions contained within it.
46+ ///
47+ /// * In most cases, you want to use [`Self::check_next_block`] instead to perform a full verification.
48+ ///
49+ /// * This will reject any blocks with a height <= the current height and any blocks with a height >= the current height + GC.
50+ /// For the former, a valid block already exists and,
51+ /// for the latter, the comittte is still unknown.
52+ pub fn check_block_subdag ( & self , block : Block < N > , pending_blocks : & [ PendingBlock < N > ] ) -> Result < PendingBlock < N > > {
53+ self . check_block_subdag_inner ( & block, pending_blocks) ?;
54+ Ok ( PendingBlock ( block) )
55+ }
56+
57+ fn check_block_subdag_inner ( & self , block : & Block < N > , pending_blocks : & [ PendingBlock < N > ] ) -> Result < ( ) > {
58+ // First check that the heights and hashes of the pending block sequence and of the new block are correct.
59+ // The hash checks should be redundant, but we perform them out of extra caution.
60+ let mut expected_height = self . latest_height ( ) + 1 ;
61+ for pending in pending_blocks {
62+ if pending. height ( ) != expected_height {
63+ bail ! (
64+ "Pending block has invalid height. Expected {expected_height}, but got {actual}." ,
65+ actual = pending. height( )
66+ ) ;
67+ }
3068
31- // Check that this is actually the next block.
32- if height != latest_block. height ( ) + 1 {
33- bail ! ( "Block height is {height}, but expected {}" , latest_block. height( ) + 1 ) ;
69+ if self . contains_block_hash ( & pending. hash ( ) ) ? {
70+ bail ! ( "Hash for pending block '{}' already exists in the ledger" , block. hash( ) )
71+ }
72+
73+ expected_height += 1 ;
3474 }
3575
36- // Ensure the block hash does not already exist.
3776 if self . contains_block_hash ( & block. hash ( ) ) ? {
3877 bail ! ( "Block hash '{}' already exists in the ledger" , block. hash( ) )
3978 }
4079
80+ if block. height ( ) != expected_height {
81+ bail ! ( "Block has invalid height. Expected {expected_height}, but got {}." , block. height( ) ) ;
82+ }
83+
84+ // Ensure the certificates in the block subdag have met quorum requirements.
85+ self . check_block_subdag_quorum ( block) ?;
86+
87+ // Determine if the block subdag is correctly constructed and is not a combination of multiple subdags.
88+ self . check_block_subdag_atomicity ( block) ?;
89+
90+ // Ensure that all leaves of the subdag point to valid batches in other subdags/blocks.
91+ self . check_block_subdag_leaves ( block, pending_blocks) ?;
92+
93+ Ok ( ( ) )
94+ }
95+
96+ /// Checks the given block is a valid next block with regard to the current state/height of the Ledger.
97+ ///
98+ /// # Panics
99+ /// This function panics if called from an async context.
100+ pub fn check_next_block < R : CryptoRng + Rng > ( & self , block : & Block < N > , rng : & mut R ) -> Result < ( ) > {
101+ self . check_block_subdag_inner ( block, & [ ] ) ?;
102+ self . check_block_content_inner ( block, rng) ?;
103+
104+ Ok ( ( ) )
105+ }
106+
107+ /// Takes a pending block and performs the remaining checks to full verify it.
108+ ///
109+ /// # Arguments
110+ /// This takes a [`PendingBlock`] as input, which is the output of a successful call to [`Self::check_block_subdag`].
111+ /// The latter already verified the block's DAG and certificate signatures.
112+ ///
113+ /// # Return Value
114+ /// This returns a [`Block`] on success representing the fully verified block.
115+ ///
116+ /// # Panics
117+ /// This function panics if called from an async context.
118+ pub fn check_block_content < R : CryptoRng + Rng > ( & self , block : PendingBlock < N > , rng : & mut R ) -> Result < Block < N > > {
119+ self . check_block_content_inner ( & block. 0 , rng) ?;
120+ Ok ( block. 0 )
121+ }
122+
123+ /// # Panics
124+ /// This function panics if called from an async context.
125+ fn check_block_content_inner < R : CryptoRng + Rng > ( & self , block : & Block < N > , rng : & mut R ) -> Result < ( ) > {
126+ let latest_block = self . latest_block ( ) ;
127+
41128 // Ensure the solutions do not already exist.
42129 for solution_id in block. solutions ( ) . solution_ids ( ) {
43130 if self . contains_solution_id ( solution_id) ? {
@@ -115,15 +202,6 @@ impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
115202 }
116203 }
117204
118- // Ensure the certificates in the block subdag have met quorum requirements.
119- self . check_block_subdag_quorum ( block) ?;
120-
121- // Determine if the block subdag is correctly constructed and is not a combination of multiple subdags.
122- self . check_block_subdag_atomicity ( block) ?;
123-
124- // Ensure that all leaves of the subdag point to valid batches in other subdags/blocks.
125- self . check_block_subdag_leaves ( block) ?;
126-
127205 // Ensure that each existing solution ID from the block exists in the ledger.
128206 for existing_solution_id in expected_existing_solution_ids {
129207 if !self . contains_solution_id ( & existing_solution_id) ? {
@@ -145,12 +223,21 @@ impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
145223 ///
146224 /// This does not verify that the batches are signed correctly or that the edges are valid
147225 /// (only point to the previous round), as those checks already happened when the node received the batch.
148- fn check_block_subdag_leaves ( & self , block : & Block < N > ) -> Result < ( ) > {
226+ fn check_block_subdag_leaves ( & self , block : & Block < N > , previous_blocks : & [ PendingBlock < N > ] ) -> Result < ( ) > {
149227 // Check if the block has a subdag.
150228 let Authority :: Quorum ( subdag) = block. authority ( ) else {
151229 return Ok ( ( ) ) ;
152230 } ;
153231
232+ let previous_certs: HashSet < _ > = previous_blocks
233+ . iter ( )
234+ . filter_map ( |block| match block. authority ( ) {
235+ Authority :: Quorum ( subdag) => Some ( subdag. certificate_ids ( ) ) ,
236+ Authority :: Beacon ( _) => None ,
237+ } )
238+ . flatten ( )
239+ . collect ( ) ;
240+
154241 // Store the IDs of all certificates in this subDAG.
155242 // This allows determining which edges point to other subDAGs/blocks.
156243 let subdag_certs: HashSet < _ > = subdag. certificate_ids ( ) . collect ( ) ;
@@ -173,7 +260,7 @@ impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
173260 }
174261
175262 // Ensure that the certificate is associated with a previous block.
176- if !self . vm . block_store ( ) . contains_block_for_certificate ( prev_id) ? {
263+ if !previous_certs . contains ( prev_id ) && ! self . vm . block_store ( ) . contains_block_for_certificate ( prev_id) ? {
177264 bail ! (
178265 "Batch(es) in the block point(s) to a certificate {prev_id} in round {prev_round} that is not associated with a previous block"
179266 )
@@ -184,6 +271,8 @@ impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
184271 }
185272
186273 /// Check that the certificates in the block subdag have met quorum requirements.
274+ ///
275+ /// Called by [`Self::check_block_subdag`]
187276 fn check_block_subdag_quorum ( & self , block : & Block < N > ) -> Result < ( ) > {
188277 // Check if the block has a subdag.
189278 let subdag = match block. authority ( ) {
@@ -226,6 +315,8 @@ impl<N: Network, C: ConsensusStorage<N>> Ledger<N, C> {
226315 }
227316
228317 /// Checks that the block subdag can not be split into multiple valid subdags.
318+ ///
319+ /// Called by [`Self::check_block_subdag`]
229320 fn check_block_subdag_atomicity ( & self , block : & Block < N > ) -> Result < ( ) > {
230321 // Returns `true` if there is a path from the previous certificate to the current certificate.
231322 fn is_linked < N : Network > (
0 commit comments