@@ -30,8 +30,12 @@ pub struct ImmutableAddressStore {
3030}
3131
3232impl ImmutableAddressStore {
33- pub fn new ( path : impl AsRef < Path > ) -> Result < Self > {
34- let cfg = fjall:: Config :: new ( path) . max_write_buffer_size ( 512 * 1024 * 1024 ) . temporary ( true ) ;
33+ pub fn new ( path : impl AsRef < Path > , clear_on_start : bool ) -> Result < Self > {
34+ let path = path. as_ref ( ) ;
35+ if path. exists ( ) && clear_on_start {
36+ std:: fs:: remove_dir_all ( path) ?;
37+ }
38+ let cfg = fjall:: Config :: new ( path) . max_write_buffer_size ( 512 * 1024 * 1024 ) ;
3539 let keyspace = Keyspace :: open ( cfg) ?;
3640
3741 let utxos = keyspace. open_partition ( "address_utxos" , PartitionCreateOptions :: default ( ) ) ?;
@@ -52,15 +56,36 @@ impl ImmutableAddressStore {
5256 /// for an entire epoch. Skips any partitions that have already stored the given epoch.
5357 /// All writes are batched and committed atomically, preventing on-disk corruption in case of failure.
5458 pub async fn persist_epoch ( & self , epoch : u64 , config : & AddressStorageConfig ) -> Result < ( ) > {
55- let persist_utxos = config. store_info
56- && !self . epoch_exists ( self . utxos . clone ( ) , ADDRESS_UTXOS_EPOCH_COUNTER , epoch) . await ?;
57- let persist_txs = config. store_transactions
58- && !self . epoch_exists ( self . txs . clone ( ) , ADDRESS_TXS_EPOCH_COUNTER , epoch) . await ?;
59- let persist_totals = config. store_totals
60- && !self . epoch_exists ( self . totals . clone ( ) , ADDRESS_TOTALS_EPOCH_COUNTER , epoch) . await ?;
59+ // Skip if all options disabled
60+ if !( config. store_info || config. store_transactions || config. store_totals ) {
61+ debug ! ( "no persistence needed for epoch {epoch} (all stores disabled)" ) ;
62+ return Ok ( ( ) ) ;
63+ }
64+
65+ // Determine which partitions need persistence
66+ let ( persist_utxos, persist_txs, persist_totals) = if config. clear_on_start {
67+ (
68+ config. store_info ,
69+ config. store_transactions ,
70+ config. store_totals ,
71+ )
72+ } else {
73+ let utxos = config. store_info
74+ && !self
75+ . epoch_exists ( self . utxos . clone ( ) , ADDRESS_UTXOS_EPOCH_COUNTER , epoch)
76+ . await ?;
77+ let txs = config. store_transactions
78+ && !self . epoch_exists ( self . txs . clone ( ) , ADDRESS_TXS_EPOCH_COUNTER , epoch) . await ?;
79+ let totals = config. store_totals
80+ && !self
81+ . epoch_exists ( self . totals . clone ( ) , ADDRESS_TOTALS_EPOCH_COUNTER , epoch)
82+ . await ?;
83+ ( utxos, txs, totals)
84+ } ;
6185
86+ // Skip if all partitions have already been persisted for the epoch
6287 if !( persist_utxos || persist_txs || persist_totals) {
63- debug ! ( "no persistence needed for epoch {epoch} (already persisted or disabled) " ) ;
88+ debug ! ( "no persistence needed for epoch {epoch}" ) ;
6489 return Ok ( ( ) ) ;
6590 }
6691
@@ -120,22 +145,14 @@ impl ImmutableAddressStore {
120145 }
121146
122147 // Metadata markers
123- if persist_utxos {
124- batch. insert (
125- & self . utxos ,
126- ADDRESS_UTXOS_EPOCH_COUNTER ,
127- epoch. to_le_bytes ( ) ,
128- ) ;
129- }
130- if persist_txs {
131- batch. insert ( & self . txs , ADDRESS_TXS_EPOCH_COUNTER , epoch. to_le_bytes ( ) ) ;
132- }
133- if persist_totals {
134- batch. insert (
135- & self . totals ,
136- ADDRESS_TOTALS_EPOCH_COUNTER ,
137- epoch. to_le_bytes ( ) ,
138- ) ;
148+ for ( enabled, part, key) in [
149+ ( persist_utxos, & self . utxos , ADDRESS_UTXOS_EPOCH_COUNTER ) ,
150+ ( persist_txs, & self . txs , ADDRESS_TXS_EPOCH_COUNTER ) ,
151+ ( persist_totals, & self . totals , ADDRESS_TOTALS_EPOCH_COUNTER ) ,
152+ ] {
153+ if enabled {
154+ batch. insert ( part, key, epoch. to_le_bytes ( ) ) ;
155+ }
139156 }
140157
141158 match batch. commit ( ) {
@@ -158,13 +175,18 @@ impl ImmutableAddressStore {
158175 pub async fn get_utxos ( & self , address : & Address ) -> Result < Option < Vec < UTxOIdentifier > > > {
159176 let key = address. to_bytes_key ( ) ?;
160177
178+ let db_raw = self . utxos . get ( & key) ?;
179+ let db_had_key = db_raw. is_some ( ) ;
180+
161181 let mut live: Vec < UTxOIdentifier > =
162- self . utxos . get ( & key ) ? . map ( |bytes| decode ( & bytes) ) . transpose ( ) ?. unwrap_or_default ( ) ;
182+ db_raw . map ( |bytes| decode ( & bytes) ) . transpose ( ) ?. unwrap_or_default ( ) ;
163183
164184 let pending = self . pending . lock ( ) . await ;
185+ let mut pending_touched = false ;
165186 for block_map in pending. iter ( ) {
166187 if let Some ( entry) = block_map. get ( address) {
167188 if let Some ( deltas) = & entry. utxos {
189+ pending_touched = true ;
168190 for delta in deltas {
169191 match delta {
170192 UtxoDelta :: Created ( u) => live. push ( * u) ,
@@ -175,8 +197,13 @@ impl ImmutableAddressStore {
175197 }
176198 }
177199
200+ // Only return None if the address never existed
178201 if live. is_empty ( ) {
179- Ok ( None )
202+ if db_had_key || pending_touched {
203+ Ok ( Some ( vec ! [ ] ) )
204+ } else {
205+ Ok ( None )
206+ }
180207 } else {
181208 Ok ( Some ( live) )
182209 }
0 commit comments