@@ -91,6 +91,165 @@ impl<PersistedState: ChainStore> ChainState<PersistedState> {
9191 }
9292 Ok ( true )
9393 }
94+ #[ inline]
95+ /// Whether a node is the genesis block for this net
96+ fn is_genesis ( & self , header : & BlockHeader ) -> bool {
97+ header. block_hash ( ) == self . chain_params ( ) . genesis . block_hash ( )
98+ }
99+ #[ inline]
100+ /// Returns the ancestor of a given block
101+ fn get_ancestor ( & self , header : & BlockHeader ) -> Result < DiskBlockHeader , BlockchainError > {
102+ self . get_disk_block_header ( & header. prev_blockhash )
103+ }
104+ /// Returns the cumulative work in this branch
105+ fn get_branch_work ( & self , header : & BlockHeader ) -> Result < Uint256 , BlockchainError > {
106+ let mut header = header. clone ( ) ;
107+ let mut work = Uint256 :: from_u64 ( 0 ) . unwrap ( ) ;
108+ while !self . is_genesis ( & header) {
109+ work = work + header. work ( ) ;
110+ header = * self . get_ancestor ( & header) ?;
111+ }
112+
113+ Ok ( work)
114+ }
115+ fn check_branch ( & self , branch_tip : & BlockHeader ) -> Result < ( ) , BlockchainError > {
116+ let mut header = self . get_disk_block_header ( & branch_tip. block_hash ( ) ) ?;
117+
118+ while !self . is_genesis ( & header) {
119+ header = self . get_ancestor ( & header) ?;
120+ match header {
121+ DiskBlockHeader :: Orphan ( block) => {
122+ return Err ( BlockchainError :: InvalidTip ( format ! (
123+ "Block {} doesn't have a known ancestor (i.e an orphan block)" ,
124+ block. block_hash( )
125+ ) ) )
126+ }
127+ _ => { /* do nothing */ }
128+ }
129+ }
130+
131+ Ok ( ( ) )
132+ }
133+ fn get_chain_depth ( & self , branch_tip : & BlockHeader ) -> Result < u32 , BlockchainError > {
134+ let mut header = self . get_disk_block_header ( & branch_tip. block_hash ( ) ) ?;
135+
136+ let mut counter = 0 ;
137+ while !self . is_genesis ( & header) {
138+ header = self . get_ancestor ( & header) ?;
139+ counter += 1 ;
140+ }
141+
142+ Ok ( counter)
143+ }
144+ fn mark_chain_as_active ( & self , new_tip : & BlockHeader ) -> Result < ( ) , BlockchainError > {
145+ let mut header = self . get_disk_block_header ( & new_tip. block_hash ( ) ) ?;
146+ let height = self . get_chain_depth ( new_tip) ?;
147+ let inner = read_lock ! ( self ) ;
148+ while !self . is_genesis ( & header) {
149+ header = self . get_ancestor ( & header) ?;
150+ inner
151+ . chainstore
152+ . update_block_index ( height, header. block_hash ( ) ) ?;
153+ let new_header = DiskBlockHeader :: HeadersOnly ( * header, height) ;
154+ inner. chainstore . save_header ( & new_header) ?;
155+ }
156+
157+ Ok ( ( ) )
158+ }
159+ /// Mark the current index as inactive, either because we found an invalid ancestor,
160+ /// or we are in the middle of reorg
161+ fn mark_chain_as_inactive ( & self , new_tip : & BlockHeader ) -> Result < ( ) , BlockchainError > {
162+ let mut header = self . get_disk_block_header ( & new_tip. block_hash ( ) ) ?;
163+ let inner = read_lock ! ( self ) ;
164+ while !self . is_genesis ( & header) {
165+ header = self . get_ancestor ( & header) ?;
166+ let new_header = DiskBlockHeader :: InFork ( * header) ;
167+ inner. chainstore . save_header ( & new_header) ?;
168+ }
169+
170+ Ok ( ( ) )
171+ }
172+ // This method should only be called after we validate the new branch
173+ fn reorg ( & self , new_tip : BlockHeader ) -> Result < ( ) , BlockchainError > {
174+ let current_best_block = self . get_best_block ( ) . unwrap ( ) . 1 ;
175+ let current_best_block = self . get_block_header ( & current_best_block) ?;
176+ self . mark_chain_as_active ( & new_tip) ?;
177+ self . mark_chain_as_inactive ( & current_best_block) ?;
178+
179+ let mut inner = self . inner . write ( ) . unwrap ( ) ;
180+ inner. best_block . best_block = new_tip. block_hash ( ) ;
181+ inner. best_block . validation_index = self . get_last_valid_block ( & new_tip) ?;
182+ Ok ( ( ) )
183+ }
184+
185+ /// Grabs the last block we validated in this branch. We don't validate a fork, unless it
186+ /// becomes the best chain. This function technically finds out what is the last common block
187+ /// between two branches.
188+ fn get_last_valid_block ( & self , header : & BlockHeader ) -> Result < BlockHash , BlockchainError > {
189+ let mut header = self . get_disk_block_header ( & header. block_hash ( ) ) ?;
190+
191+ while !self . is_genesis ( & header) {
192+ match header {
193+ DiskBlockHeader :: FullyValid ( _, _) => return Ok ( header. block_hash ( ) ) ,
194+ DiskBlockHeader :: Orphan ( _) => {
195+ return Err ( BlockchainError :: InvalidTip ( format ! (
196+ "Block {} doesn't have a known ancestor (i.e an orphan block)" ,
197+ header. block_hash( )
198+ ) ) )
199+ }
200+ DiskBlockHeader :: HeadersOnly ( _, _) | DiskBlockHeader :: InFork ( _) => { }
201+ }
202+ header = self . get_ancestor ( & header) ?;
203+ }
204+
205+ unreachable ! ( )
206+ }
207+ /// If we get a header that doesn't build on top of our best chain, it may cause a reorganization.
208+ /// We check this here.
209+ pub fn maybe_reorg ( & self , branch_tip : BlockHeader ) -> Result < ( ) , BlockchainError > {
210+ let current_tip = self . get_block_header ( & self . get_best_block ( ) . unwrap ( ) . 1 ) ?;
211+ self . check_branch ( & branch_tip) ?;
212+
213+ let current_work = self . get_branch_work ( & current_tip) ?;
214+ let new_work = self . get_branch_work ( & branch_tip) ?;
215+
216+ if new_work > current_work {
217+ self . reorg ( branch_tip) ?;
218+ return Ok ( ( ) ) ;
219+ }
220+ self . push_alt_tip ( & branch_tip) ?;
221+
222+ read_lock ! ( self )
223+ . chainstore
224+ . save_header ( & super :: chainstore:: DiskBlockHeader :: InFork ( branch_tip) ) ?;
225+ Ok ( ( ) )
226+ }
227+ /// Stores a new tip for a branch that is not the best one
228+ fn push_alt_tip ( & self , branch_tip : & BlockHeader ) -> Result < ( ) , BlockchainError > {
229+ let ancestor = self . get_ancestor ( branch_tip) ;
230+ let ancestor = match ancestor {
231+ Ok ( ancestor) => Some ( ancestor) ,
232+ Err ( BlockchainError :: BlockNotPresent ) => None ,
233+ Err ( e) => return Err ( e) ,
234+ } ;
235+ let mut inner = write_lock ! ( self ) ;
236+ if ancestor. is_some ( ) {
237+ let ancestor_hash = ancestor. unwrap ( ) . block_hash ( ) ;
238+ if let Some ( idx) = inner
239+ . best_block
240+ . alternative_tips
241+ . iter ( )
242+ . position ( |hash| ancestor_hash == * hash)
243+ {
244+ inner. best_block . alternative_tips . remove ( idx) ;
245+ }
246+ }
247+ inner
248+ . best_block
249+ . alternative_tips
250+ . push ( branch_tip. block_hash ( ) ) ;
251+ Ok ( ( ) )
252+ }
94253 fn calc_next_work_required (
95254 last_block : & BlockHeader ,
96255 first_block : & BlockHeader ,
@@ -239,11 +398,15 @@ impl<PersistedState: ChainStore> ChainState<PersistedState> {
239398 ) -> ChainState < KvChainStore > {
240399 let genesis = genesis_block ( network) ;
241400 chainstore
242- . save_header (
243- & super :: chainstore :: DiskBlockHeader :: FullyValid ( genesis. header , 0 ) ,
401+ . save_header ( & super :: chainstore :: DiskBlockHeader :: FullyValid (
402+ genesis. header ,
244403 0 ,
245- )
404+ ) )
246405 . expect ( "Error while saving genesis" ) ;
406+ chainstore
407+ . update_block_index ( 0 , genesis. block_hash ( ) )
408+ . expect ( "Error updating index" ) ;
409+
247410 let assume_valid_hash = Self :: get_assume_valid_value ( network, assume_valid) ;
248411 ChainState {
249412 inner : RwLock :: new ( ChainStateInner {
@@ -462,6 +625,7 @@ impl<PersistedState: ChainStore> BlockchainProviderInterface for ChainState<Pers
462625 return Ok ( ( ) ) ;
463626 }
464627 DiskBlockHeader :: Orphan ( _) => return Ok ( ( ) ) ,
628+ DiskBlockHeader :: InFork ( _) => return Ok ( ( ) ) ,
465629 DiskBlockHeader :: HeadersOnly ( _, height) => height,
466630 } ;
467631 let inner = self . inner . read ( ) . unwrap ( ) ;
@@ -501,10 +665,15 @@ impl<PersistedState: ChainStore> BlockchainProviderInterface for ChainState<Pers
501665 // Notify others we have a new block
502666 async_std:: task:: block_on ( self . notify ( Notification :: NewBlock ( ( block. to_owned ( ) , height) ) ) ) ;
503667
504- inner. chainstore . save_header (
505- & super :: chainstore:: DiskBlockHeader :: FullyValid ( block. header , height) ,
506- height,
507- ) ?;
668+ inner
669+ . chainstore
670+ . save_header ( & super :: chainstore:: DiskBlockHeader :: FullyValid (
671+ block. header ,
672+ height,
673+ ) ) ?;
674+ inner
675+ . chainstore
676+ . update_block_index ( height, block. block_hash ( ) ) ?;
508677 inner. chainstore . save_height ( & inner. best_block ) ?;
509678 // Drop this lock because we need a write lock to inner, if we hold this lock this will
510679 // cause a deadlock.
@@ -518,10 +687,6 @@ impl<PersistedState: ChainStore> BlockchainProviderInterface for ChainState<Pers
518687 Ok ( ( ) )
519688 }
520689
521- fn handle_reorg ( & self ) -> super :: Result < ( ) > {
522- todo ! ( )
523- }
524-
525690 fn handle_transaction ( & self ) -> super :: Result < ( ) > {
526691 unimplemented ! ( "This chain_state has no mempool" )
527692 }
@@ -553,21 +718,23 @@ impl<PersistedState: ChainStore> BlockchainProviderInterface for ChainState<Pers
553718 let prev_block = self . get_block_header ( & best_block. best_block ) ?;
554719 // Check pow
555720 let target = self . get_next_required_work ( & prev_block, height) ;
556- let _ = header. validate_pow ( & target) . map_err ( |_| {
721+ let block_hash = header. validate_pow ( & target) . map_err ( |_| {
557722 BlockchainError :: BlockValidationError ( BlockValidationErrors :: NotEnoughPow )
558723 } ) ?;
559- inner. chainstore . save_header (
560- & super :: chainstore:: DiskBlockHeader :: HeadersOnly ( header, height) ,
561- height,
562- ) ?;
724+ inner
725+ . chainstore
726+ . save_header ( & super :: chainstore:: DiskBlockHeader :: HeadersOnly (
727+ header, height,
728+ ) ) ?;
729+ inner. chainstore . update_block_index ( height, block_hash) ?;
563730 drop ( inner) ;
564731 let mut inner = self . inner . write ( ) . unwrap ( ) ;
565- inner. best_block . new_block ( header . block_hash ( ) , height) ;
732+ inner. best_block . new_block ( block_hash, height) ;
566733 if header. block_hash ( ) == inner. assume_valid . 0 {
567734 inner. assume_valid . 1 = height;
568735 }
569736 } else {
570- unimplemented ! ( ) ;
737+ self . maybe_reorg ( header ) ? ;
571738 }
572739 Ok ( ( ) )
573740 }
@@ -579,7 +746,7 @@ impl<PersistedState: ChainStore> BlockchainProviderInterface for ChainState<Pers
579746 match header {
580747 DiskBlockHeader :: HeadersOnly ( _, height) => Ok ( height) ,
581748 DiskBlockHeader :: FullyValid ( _, height) => Ok ( height) ,
582- DiskBlockHeader :: Orphan ( _ ) => unreachable ! ( ) ,
749+ _ => unreachable ! ( ) ,
583750 }
584751 }
585752}
@@ -597,6 +764,7 @@ macro_rules! write_lock {
597764 $obj. inner. write( ) . expect( "get_block_hash: Poisoned lock" )
598765 } ;
599766}
767+
600768#[ derive( Clone ) ]
601769/// Internal representation of the chain we are in
602770pub struct BestChain {
0 commit comments