@@ -11,12 +11,23 @@ use std::sync::{Arc, RwLock};
1111
1212#[ derive( Debug , Clone , Serialize , Deserialize , PartialEq ) ]
1313pub struct HyperlaneSnapshot {
14+ // the trusted EV height in the ZKISM
1415 pub height : u64 ,
16+ // the Hyperlane Message Tree e.g. Snapshot
1517 pub tree : MerkleTree ,
18+ // whether this Snapshot has been finalized
19+ pub finalized : bool ,
1620}
1721impl HyperlaneSnapshot {
1822 pub fn new ( height : u64 , tree : MerkleTree ) -> HyperlaneSnapshot {
19- HyperlaneSnapshot { height, tree }
23+ HyperlaneSnapshot {
24+ height,
25+ tree,
26+ finalized : false ,
27+ }
28+ }
29+ pub fn finalize ( & mut self ) {
30+ self . finalized = true ;
2031 }
2132}
2233
@@ -53,6 +64,7 @@ impl HyperlaneSnapshotStore {
5364 Ok ( vec ! [ ColumnFamilyDescriptor :: new( "snapshots" , Options :: default ( ) ) ] )
5465 }
5566
67+ /// Insert a Hyperlane Snapshot into the database
5668 pub fn insert_snapshot ( & self , index : u64 , snapshot : HyperlaneSnapshot ) -> Result < ( ) > {
5769 // Serialize outside the lock to minimize lock duration
5870 let serialized = bincode:: serialize ( & snapshot) . context ( "Failed to serialize snapshot" ) ?;
@@ -70,6 +82,7 @@ impl HyperlaneSnapshotStore {
7082 Ok ( ( ) )
7183 }
7284
85+ /// Get a Hyperlane Snapshot by index
7386 pub fn get_snapshot ( & self , index : u64 ) -> Result < HyperlaneSnapshot > {
7487 let read_lock = self . db . read ( ) . map_err ( |e| anyhow:: anyhow!( "lock error: {e}" ) ) ?;
7588 let cf = read_lock. cf_handle ( "snapshots" ) . context ( "Missing CF" ) ?;
@@ -88,6 +101,49 @@ impl HyperlaneSnapshotStore {
88101 Ok ( snapshot)
89102 }
90103
104+ /// Get the latest pending snapshot, we expect only the most recent snapshot to be unfinalized
105+ pub fn get_pending_snapshot ( & self ) -> Result < Option < ( u64 , HyperlaneSnapshot ) > > {
106+ let read_lock = self
107+ . db
108+ . read ( )
109+ . map_err ( |e| anyhow:: anyhow!( "Failed to acquire read lock: {e}" ) ) ?;
110+ let cf = read_lock. cf_handle ( "snapshots" ) . context ( "Missing CF" ) ?;
111+ let mut iter = read_lock. iterator_cf ( cf, IteratorMode :: End ) ;
112+ while let Some ( Ok ( ( k, v) ) ) = iter. next ( ) {
113+ if k. len ( ) != 8 {
114+ continue ;
115+ }
116+ let mut buf = [ 0u8 ; 8 ] ;
117+ buf. copy_from_slice ( & k) ;
118+ let index = u64:: from_be_bytes ( buf) ;
119+ let mut snapshot: HyperlaneSnapshot = bincode:: deserialize ( & v) . context ( "Failed to deserialize snapshot" ) ?;
120+ for h in snapshot. tree . branch . iter_mut ( ) {
121+ if h. is_empty ( ) {
122+ * h = ZERO_BYTES . to_string ( ) ;
123+ }
124+ }
125+ if !snapshot. finalized {
126+ return Ok ( Some ( ( index, snapshot) ) ) ;
127+ }
128+ }
129+ Ok ( None )
130+ }
131+
132+ /// Finalize a Hyperlane Snapshot after successful proof submission
133+ pub fn finalize_snapshot ( & self , index : u64 ) -> Result < ( ) > {
134+ let mut snapshot = self
135+ . get_snapshot ( index)
136+ . with_context ( || format ! ( "Snapshot at index {index} not found" ) ) ?;
137+ if snapshot. finalized {
138+ return Err ( anyhow:: anyhow!(
139+ "Tried to finalize a finalized snapshot at index {index}"
140+ ) ) ;
141+ }
142+ snapshot. finalized = true ;
143+ self . insert_snapshot ( index, snapshot)
144+ }
145+
146+ /// Get the next insert index for the Hyperlane Snapshot store
91147 pub fn current_index ( & self ) -> Result < u64 > {
92148 let read_lock = self
93149 . db
@@ -104,6 +160,7 @@ impl HyperlaneSnapshotStore {
104160 }
105161 }
106162
163+ /// Reset the database by dropping the snapshots column family and creating a new one
107164 pub fn reset_db ( & self ) -> Result < ( ) > {
108165 let mut write_lock = self
109166 . db
@@ -115,3 +172,46 @@ impl HyperlaneSnapshotStore {
115172 Ok ( ( ) )
116173 }
117174}
175+
176+ #[ cfg( test) ]
177+ mod tests {
178+ use super :: * ;
179+
180+ #[ test]
181+ fn test_insert_snapshot ( ) {
182+ let store = HyperlaneSnapshotStore :: new ( tempfile:: tempdir ( ) . unwrap ( ) , None ) . unwrap ( ) ;
183+ let snapshot = HyperlaneSnapshot :: new ( 0 , MerkleTree :: default ( ) ) ;
184+ store. insert_snapshot ( 0 , snapshot) . unwrap ( ) ;
185+ }
186+ #[ test]
187+ fn test_get_snapshot ( ) {
188+ let store = HyperlaneSnapshotStore :: new ( tempfile:: tempdir ( ) . unwrap ( ) , None ) . unwrap ( ) ;
189+ let snapshot = HyperlaneSnapshot :: new ( 0 , MerkleTree :: default ( ) ) ;
190+ store. insert_snapshot ( 0 , snapshot. clone ( ) ) . unwrap ( ) ;
191+ let retrieved_snapshot = store. get_snapshot ( 0 ) . unwrap ( ) ;
192+ assert_eq ! ( retrieved_snapshot, snapshot) ;
193+ }
194+ #[ test]
195+ fn test_get_pending_snapshot ( ) {
196+ let store = HyperlaneSnapshotStore :: new ( tempfile:: tempdir ( ) . unwrap ( ) , None ) . unwrap ( ) ;
197+ let first_snapshot = HyperlaneSnapshot :: new ( 0 , MerkleTree :: default ( ) ) ;
198+ let second_snapshot = HyperlaneSnapshot :: new ( 1 , MerkleTree :: default ( ) ) ;
199+ let third_snapshot = HyperlaneSnapshot :: new ( 2 , MerkleTree :: default ( ) ) ;
200+ store. insert_snapshot ( 0 , first_snapshot. clone ( ) ) . unwrap ( ) ;
201+ store. insert_snapshot ( 1 , second_snapshot. clone ( ) ) . unwrap ( ) ;
202+ store. insert_snapshot ( 2 , third_snapshot. clone ( ) ) . unwrap ( ) ;
203+ store. finalize_snapshot ( 0 ) . unwrap ( ) ;
204+ store. finalize_snapshot ( 1 ) . unwrap ( ) ;
205+ let retrieved_snapshot = store. get_pending_snapshot ( ) . unwrap ( ) ;
206+ assert_eq ! ( retrieved_snapshot, Some ( ( 2 , third_snapshot) ) ) ;
207+ }
208+ #[ test]
209+ fn test_finalize_snapshot ( ) {
210+ let store = HyperlaneSnapshotStore :: new ( tempfile:: tempdir ( ) . unwrap ( ) , None ) . unwrap ( ) ;
211+ let snapshot = HyperlaneSnapshot :: new ( 0 , MerkleTree :: default ( ) ) ;
212+ store. insert_snapshot ( 0 , snapshot. clone ( ) ) . unwrap ( ) ;
213+ store. finalize_snapshot ( 0 ) . unwrap ( ) ;
214+ let retrieved_snapshot = store. get_snapshot ( 0 ) . unwrap ( ) ;
215+ assert ! ( retrieved_snapshot. finalized) ;
216+ }
217+ }
0 commit comments