@@ -6,13 +6,6 @@ use bitcoin::BlockHash;
66use crate :: { BlockId , ChainOracle } ;
77
88/// This is a local implementation of [`ChainOracle`].
9- ///
10- /// TODO: We need a cache/snapshot thing for chain oracle.
11- /// * Minimize calls to remotes.
12- /// * Can we cache it forever? Should we drop stuff?
13- /// * Assume anything deeper than (i.e. 10) blocks won't be reorged.
14- /// * Is this a cache on txs or block? or both?
15- /// TODO: Parents of children are confirmed if children are confirmed.
169#[ derive( Debug , Default , Clone , PartialEq , Eq , PartialOrd , Ord ) ]
1710pub struct LocalChain {
1811 blocks : BTreeMap < u32 , BlockHash > ,
@@ -71,20 +64,18 @@ impl LocalChain {
7164 }
7265 }
7366
67+ /// Get a reference to a map of block height to hash.
68+ pub fn blocks ( & self ) -> & BTreeMap < u32 , BlockHash > {
69+ & self . blocks
70+ }
71+
7472 pub fn tip ( & self ) -> Option < BlockId > {
7573 self . blocks
7674 . iter ( )
7775 . last ( )
7876 . map ( |( & height, & hash) | BlockId { height, hash } )
7977 }
8078
81- /// Get a block at the given height.
82- pub fn get_block ( & self , height : u32 ) -> Option < BlockId > {
83- self . blocks
84- . get ( & height)
85- . map ( |& hash| BlockId { height, hash } )
86- }
87-
8879 /// This is like the sparsechain's logic, expect we must guarantee that all invalidated heights
8980 /// are to be re-filled.
9081 pub fn determine_changeset ( & self , update : & Self ) -> Result < ChangeSet , UpdateNotConnectedError > {
@@ -173,6 +164,31 @@ impl LocalChain {
173164 pub fn heights ( & self ) -> BTreeSet < u32 > {
174165 self . blocks . keys ( ) . cloned ( ) . collect ( )
175166 }
167+
168+ /// Insert a block of [`BlockId`] into the [`LocalChain`].
169+ ///
170+ /// # Error
171+ ///
172+ /// If the insertion height already contains a block, and the block has a different blockhash,
173+ /// this will result in an [`InsertBlockNotMatchingError`].
174+ pub fn insert_block (
175+ & mut self ,
176+ block_id : BlockId ,
177+ ) -> Result < ChangeSet , InsertBlockNotMatchingError > {
178+ let mut update = Self :: from_blocks ( self . tip ( ) ) ;
179+
180+ if let Some ( original_hash) = update. blocks . insert ( block_id. height , block_id. hash ) {
181+ if original_hash != block_id. hash {
182+ return Err ( InsertBlockNotMatchingError {
183+ height : block_id. height ,
184+ original_hash,
185+ update_hash : block_id. hash ,
186+ } ) ;
187+ }
188+ }
189+
190+ Ok ( self . apply_update ( update) . expect ( "should always connect" ) )
191+ }
176192}
177193
178194/// This is the return value of [`determine_changeset`] and represents changes to [`LocalChain`].
@@ -201,3 +217,24 @@ impl core::fmt::Display for UpdateNotConnectedError {
201217
202218#[ cfg( feature = "std" ) ]
203219impl std:: error:: Error for UpdateNotConnectedError { }
220+
221+ /// Represents a failure when trying to insert a checkpoint into [`LocalChain`].
222+ #[ derive( Clone , Debug , PartialEq ) ]
223+ pub struct InsertBlockNotMatchingError {
224+ pub height : u32 ,
225+ pub original_hash : BlockHash ,
226+ pub update_hash : BlockHash ,
227+ }
228+
229+ impl core:: fmt:: Display for InsertBlockNotMatchingError {
230+ fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
231+ write ! (
232+ f,
233+ "failed to insert block at height {} as blockhashes conflict: original={}, update={}" ,
234+ self . height, self . original_hash, self . update_hash
235+ )
236+ }
237+ }
238+
239+ #[ cfg( feature = "std" ) ]
240+ impl std:: error:: Error for InsertBlockNotMatchingError { }
0 commit comments