@@ -47,6 +47,21 @@ impl LegacyTopDownHandler {
4747 height : finality. height as u64 ,
4848 block_hash : finality. block_hash ,
4949 } ;
50+ let quorum_threshold = atomically ( || self . votes . quorum_threshold ( ) ) . await ;
51+
52+ // In a single-validator subnet, self-attestation should not depend on local
53+ // cache catch-up. Otherwise aggressive proposals can be rejected by the same
54+ // node that created them, stalling consensus at one height.
55+ if quorum_threshold == 1 {
56+ let committed_height = atomically ( || {
57+ self . provider
58+ . last_committed_finality ( )
59+ . map ( |f| f. map ( |f| f. height ) . unwrap_or_default ( ) )
60+ } )
61+ . await ;
62+ return prop. height > committed_height;
63+ }
64+
5065 atomically ( || self . provider . check_proposal ( & prop) ) . await
5166 }
5267
@@ -103,37 +118,101 @@ impl LegacyTopDownHandler {
103118 async fn chain_message_from_finality_or_quorum ( & self ) -> Option < ChainMessage > {
104119 atomically ( || self . votes . pause_votes_until_find_quorum ( ) ) . await ;
105120
106- let ( parent, quorum) = atomically ( || {
121+ let ( parent, quorum, quorum_threshold ) = atomically ( || {
107122 let parent = self . provider . next_proposal ( ) ?;
108123
109124 let quorum = self
110125 . votes
111126 . find_quorum ( ) ?
112127 . map ( |( height, block_hash) | IPCParentFinality { height, block_hash } ) ;
113128
114- Ok ( ( parent, quorum) )
129+ let quorum_threshold = self . votes . quorum_threshold ( ) ?;
130+
131+ Ok ( ( parent, quorum, quorum_threshold) )
115132 } )
116133 . await ;
117134
118135 let parent = parent?;
119136
120- let quorum = if let Some ( quorum) = quorum {
121- quorum
122- } else {
123- emit ! (
124- DEBUG ,
125- ParentFinalityMissingQuorum {
126- block_height: parent. height,
127- block_hash: & hex:: encode( & parent. block_hash) ,
128- }
129- ) ;
130- return None ;
131- } ;
132-
133- let finality = if parent. height <= quorum. height {
134- parent
137+ // Fast-path for single-validator subnets: quorum threshold is 1, so requiring
138+ // a separate vote-derived quorum can unnecessarily throttle catch-up.
139+ //
140+ // In addition, bypass `next_proposal()` bounds (`max_proposal_range`, `proposal_delay`)
141+ // and favor the freshest finalized parent view queried directly from parent RPC
142+ // (`chain_head - chain_head_delay`). This avoids being throttled by local cache
143+ // catch-up speed when the node is far behind.
144+ let finality = if quorum_threshold == 1 {
145+ let committed_height = atomically ( || {
146+ self . provider
147+ . last_committed_finality ( )
148+ . map ( |f| f. map ( |f| f. height ) . unwrap_or_default ( ) )
149+ } )
150+ . await ;
151+
152+ let remote_finalized = self
153+ . provider
154+ . latest_finalized_parent_view ( )
155+ . await
156+ . ok ( )
157+ . flatten ( ) ;
158+ let candidate = if let Some ( remote_finalized) =
159+ remote_finalized. filter ( |f| f. height > committed_height)
160+ {
161+ remote_finalized
162+ } else {
163+ let latest_non_null = atomically ( || {
164+ let latest = self . provider . latest_height ( ) ?;
165+ let committed = self . provider . last_committed_finality ( ) ?;
166+
167+ let ( latest, committed_height) = match ( latest, committed) {
168+ ( Some ( latest) , Some ( committed) ) => ( latest, committed. height ) ,
169+ _ => return Ok ( None ) ,
170+ } ;
171+
172+ if latest <= committed_height {
173+ return Ok ( None ) ;
174+ }
175+
176+ let latest_non_null = self
177+ . provider
178+ . first_non_null_block ( latest) ?
179+ . filter ( |h| * h > committed_height) ;
180+
181+ let Some ( height) = latest_non_null else {
182+ return Ok ( None ) ;
183+ } ;
184+
185+ let Some ( block_hash) = self . provider . block_hash ( height) ? else {
186+ return Ok ( None ) ;
187+ } ;
188+
189+ Ok ( Some ( IPCParentFinality { height, block_hash } ) )
190+ } )
191+ . await ;
192+
193+ latest_non_null. unwrap_or ( parent)
194+ } ;
195+
196+ candidate
135197 } else {
136- quorum
198+ let quorum = if let Some ( quorum) = quorum {
199+ quorum
200+ } else {
201+ emit ! (
202+ DEBUG ,
203+ ParentFinalityMissingQuorum {
204+ block_height: parent. height,
205+ block_hash: & hex:: encode( & parent. block_hash) ,
206+ }
207+ ) ;
208+ return None ;
209+ } ;
210+
211+ if parent. height <= quorum. height {
212+ parent
213+ } else {
214+ quorum
215+ }
137216 } ;
138217
139218 Some ( ChainMessage :: Ipc ( IpcMessage :: TopDownExec ( ParentFinality {
0 commit comments