@@ -2,9 +2,9 @@ use crate::chain::{BlockHash, BlockHeader};
22use crate :: errors:: * ;
33use crate :: new_index:: BlockEntry ;
44
5+ use itertools:: Itertools ;
56use std:: collections:: HashMap ;
67use std:: fmt;
7- use std:: iter:: FromIterator ;
88use std:: slice;
99use time:: format_description:: well_known:: Rfc3339 ;
1010use time:: OffsetDateTime as DateTime ;
@@ -128,7 +128,7 @@ impl HeaderList {
128128 ) ;
129129
130130 let mut headers = HeaderList :: empty ( ) ;
131- headers. append ( headers. preprocess ( headers_chain) . 0 ) ;
131+ headers. append ( headers. preprocess ( headers_chain, & tip_hash ) . 0 ) ;
132132 headers
133133 }
134134
@@ -138,43 +138,50 @@ impl HeaderList {
138138 /// Actually applying the headers requires to first pop() the reorged blocks (if any),
139139 /// then append() the new ones.
140140 #[ trace]
141- pub fn preprocess ( & self , new_headers : Vec < BlockHeader > ) -> ( Vec < HeaderEntry > , Option < usize > ) {
141+ pub fn preprocess (
142+ & self ,
143+ new_headers : Vec < BlockHeader > ,
144+ new_tip : & BlockHash ,
145+ ) -> ( Vec < HeaderEntry > , Option < usize > ) {
142146 // header[i] -> header[i-1] (i.e. header.last() is the tip)
143- struct HashedHeader {
144- blockhash : BlockHash ,
145- header : BlockHeader ,
146- }
147- let hashed_headers =
148- Vec :: < HashedHeader > :: from_iter ( new_headers. into_iter ( ) . map ( |header| HashedHeader {
149- blockhash : header. block_hash ( ) ,
150- header,
151- } ) ) ;
152- for i in 1 ..hashed_headers. len ( ) {
153- assert_eq ! (
154- hashed_headers[ i] . header. prev_blockhash,
155- hashed_headers[ i - 1 ] . blockhash
156- ) ;
157- }
158- let prev_blockhash = match hashed_headers. first ( ) {
159- Some ( h) => h. header . prev_blockhash ,
160- None => return ( vec ! [ ] , None ) , // hashed_headers is empty
161- } ;
162- let new_height: usize = if prev_blockhash == * DEFAULT_BLOCKHASH {
163- 0
147+ let ( new_height, header_entries) = if !new_headers. is_empty ( ) {
148+ let hashed_headers = new_headers
149+ . into_iter ( )
150+ . map ( |h| ( h. block_hash ( ) , h) )
151+ . collect :: < Vec < _ > > ( ) ;
152+ for ( ( curr_blockhash, _) , ( _, next_header) ) in hashed_headers. iter ( ) . tuple_windows ( ) {
153+ assert_eq ! ( * curr_blockhash, next_header. prev_blockhash) ;
154+ }
155+ assert_eq ! ( hashed_headers. last( ) . unwrap( ) . 0 , * new_tip) ;
156+
157+ let prev_blockhash = & hashed_headers. first ( ) . unwrap ( ) . 1 . prev_blockhash ;
158+ let new_height = if * prev_blockhash == * DEFAULT_BLOCKHASH {
159+ 0
160+ } else {
161+ self . header_by_blockhash ( prev_blockhash)
162+ . expect ( "headers do not connect" )
163+ . height ( )
164+ + 1
165+ } ;
166+ let header_entries = ( new_height..)
167+ . zip ( hashed_headers)
168+ . map ( |( height, ( hash, header) ) | HeaderEntry {
169+ height,
170+ hash,
171+ header,
172+ } )
173+ . collect ( ) ;
174+ ( new_height, header_entries)
164175 } else {
165- self . header_by_blockhash ( & prev_blockhash)
166- . unwrap_or_else ( || panic ! ( "{} is not part of the blockchain" , prev_blockhash) )
176+ // No new headers, but the new tip could potentially shorten the chain (or be a no-op if it matches the existing tip)
177+ // This should not normally happen, but might due to manual `invalidateblock`
178+ let new_height = self
179+ . header_by_blockhash ( new_tip)
180+ . expect ( "new tip not in chain" )
167181 . height ( )
168- + 1
182+ + 1 ;
183+ ( new_height, vec ! [ ] )
169184 } ;
170- let header_entries = ( new_height..)
171- . zip ( hashed_headers. into_iter ( ) )
172- . map ( |( height, hashed_header) | HeaderEntry {
173- height,
174- hash : hashed_header. blockhash ,
175- header : hashed_header. header ,
176- } )
177- . collect ( ) ;
178185 let reorged_since = ( new_height < self . len ( ) ) . then_some ( new_height) ;
179186 ( header_entries, reorged_since)
180187 }
@@ -200,12 +207,9 @@ impl HeaderList {
200207 #[ trace]
201208 pub fn append ( & mut self , new_headers : Vec < HeaderEntry > ) {
202209 // new_headers[i] -> new_headers[i - 1] (i.e. new_headers.last() is the tip)
203- for i in 1 ..new_headers. len ( ) {
204- assert_eq ! ( new_headers[ i - 1 ] . height( ) + 1 , new_headers[ i] . height( ) ) ;
205- assert_eq ! (
206- * new_headers[ i - 1 ] . hash( ) ,
207- new_headers[ i] . header( ) . prev_blockhash
208- ) ;
210+ for ( curr_header, next_header) in new_headers. iter ( ) . tuple_windows ( ) {
211+ assert_eq ! ( curr_header. height( ) + 1 , next_header. height( ) ) ;
212+ assert_eq ! ( * curr_header. hash( ) , next_header. header( ) . prev_blockhash) ;
209213 }
210214 let new_height = match new_headers. first ( ) {
211215 Some ( entry) => {
0 commit comments