@@ -13,8 +13,8 @@ use crate::{
1313 Address , AmountOverflowError , Authorized , AuthorizedTransaction ,
1414 BitAssetId , BlockHash , Body , FilledOutput , FilledTransaction ,
1515 GetAddress as _, GetBitcoinValue as _, Header , InPoint , M6id , OutPoint ,
16- OutPointKey , SpentOutput , Transaction , TxData , VERSION , Verify as _ ,
17- Version , WithdrawalBundle , WithdrawalBundleStatus ,
16+ OutPointKey , AddressOutPointKey , SpentOutput , Transaction , TxData , VERSION ,
17+ Verify as _ , Version , WithdrawalBundle , WithdrawalBundleStatus ,
1818 proto:: mainchain:: TwoWayPegData ,
1919 } ,
2020 util:: Watchable ,
@@ -68,6 +68,18 @@ type WithdrawalBundlesDb = DatabaseUnique<
6868 ) > ,
6969> ;
7070
71+ #[ derive( Clone , Debug , Deserialize , Eq , PartialEq , Serialize ) ]
72+ pub struct UtxoEntry {
73+ pub created_height : u32 ,
74+ pub output : FilledOutput ,
75+ }
76+
77+ #[ derive( Clone , Debug , Deserialize , Eq , PartialEq , Serialize ) ]
78+ pub struct SpentUtxoEntry {
79+ pub created_height : u32 ,
80+ pub output : SpentOutput ,
81+ }
82+
7183#[ derive( Clone ) ]
7284pub struct State {
7385 /// Current tip
@@ -80,7 +92,9 @@ pub struct State {
8092 /// Associates Dutch auction sequence numbers with auction state
8193 dutch_auctions : dutch_auction:: Db ,
8294 utxos : DatabaseUnique < OutPointKey , SerdeBincode < FilledOutput > > ,
83- stxos : DatabaseUnique < OutPointKey , SerdeBincode < SpentOutput > > ,
95+ utxos_by_address :
96+ DatabaseUnique < AddressOutPointKey , SerdeBincode < UtxoEntry > > ,
97+ stxos : DatabaseUnique < OutPointKey , SerdeBincode < SpentUtxoEntry > > ,
8498 /// Pending withdrawal bundle. MUST exist in withdrawal_bundles
8599 pending_withdrawal_bundle : DatabaseUnique < UnitKey , SerdeBincode < M6id > > ,
86100 /// Latest failed (known) withdrawal bundle
@@ -104,7 +118,7 @@ pub struct State {
104118}
105119
106120impl State {
107- pub const NUM_DBS : u32 = bitassets:: Dbs :: NUM_DBS + 12 ;
121+ pub const NUM_DBS : u32 = bitassets:: Dbs :: NUM_DBS + 12 + 1 ;
108122
109123 pub fn new ( env : & sneed:: Env ) -> Result < Self , Error > {
110124 let mut rwtxn = env. write_txn ( ) ?;
@@ -115,6 +129,8 @@ impl State {
115129 let dutch_auctions =
116130 DatabaseUnique :: create ( env, & mut rwtxn, "dutch_auctions" ) ?;
117131 let utxos = DatabaseUnique :: create ( env, & mut rwtxn, "utxos" ) ?;
132+ let utxos_by_address =
133+ DatabaseUnique :: create ( env, & mut rwtxn, "utxos_by_address" ) ?;
118134 let stxos = DatabaseUnique :: create ( env, & mut rwtxn, "stxos" ) ?;
119135 let pending_withdrawal_bundle = DatabaseUnique :: create (
120136 env,
@@ -147,6 +163,7 @@ impl State {
147163 bitassets,
148164 dutch_auctions,
149165 utxos,
166+ utxos_by_address,
150167 stxos,
151168 pending_withdrawal_bundle,
152169 latest_failed_withdrawal_bundle,
@@ -180,10 +197,74 @@ impl State {
180197
181198 pub fn stxos (
182199 & self ,
183- ) -> & RoDatabaseUnique < OutPointKey , SerdeBincode < SpentOutput > > {
200+ ) -> & RoDatabaseUnique < OutPointKey , SerdeBincode < SpentUtxoEntry > > {
184201 & self . stxos
185202 }
186203
204+ fn put_utxo (
205+ & self ,
206+ rwtxn : & mut RwTxn ,
207+ outpoint_key : OutPointKey ,
208+ output : FilledOutput ,
209+ created_height : u32 ,
210+ ) -> Result < AddressOutPointKey , sneed:: db:: Error > {
211+ let entry = UtxoEntry { created_height, output } ;
212+ let address_key = AddressOutPointKey :: new ( entry. output . address , outpoint_key) ;
213+ self . utxos_by_address . put ( rwtxn, & address_key, & entry) ?;
214+ self . utxos . put ( rwtxn, & outpoint_key, & entry. output ) ?;
215+ Ok ( address_key)
216+ }
217+
218+ fn delete_utxo (
219+ & self ,
220+ rwtxn : & mut RwTxn ,
221+ address_key : & AddressOutPointKey
222+ ) -> Result < bool , sneed:: db:: Error > {
223+ self . utxos_by_address . delete ( rwtxn, & address_key) ?;
224+ Ok ( self . utxos . delete ( rwtxn, & address_key. outpoint_key ( ) ) ?)
225+ }
226+
227+ fn spend_utxo (
228+ & self ,
229+ rwtxn : & mut RwTxn ,
230+ outpoint_key : OutPointKey ,
231+ spent_output : SpentOutput ,
232+ ) -> Result < bool , sneed:: db:: Error > {
233+ let address_key =
234+ AddressOutPointKey :: new ( spent_output. output . address , outpoint_key) ;
235+
236+ let Some ( entry) = self . utxos_by_address . try_get ( rwtxn, & address_key) ? else {
237+ return Ok ( false ) ;
238+ } ;
239+
240+ let entry = SpentUtxoEntry {
241+ created_height : entry. created_height ,
242+ output : spent_output,
243+ } ;
244+
245+ self . stxos . put ( rwtxn, & outpoint_key, & entry) ?;
246+ self . delete_utxo ( rwtxn, & address_key)
247+ }
248+
249+ fn unspend_utxo (
250+ & self ,
251+ rwtxn : & mut RwTxn ,
252+ outpoint_key : & OutPointKey ,
253+ ) -> Result < bool , sneed:: db:: Error > {
254+ let Some ( entry) = self . stxos . try_get ( rwtxn, outpoint_key) ? else {
255+ return Ok ( false ) ;
256+ } ;
257+
258+ self . put_utxo (
259+ rwtxn,
260+ * outpoint_key,
261+ entry. output . output ,
262+ entry. created_height ,
263+ ) ?;
264+
265+ Ok ( self . stxos . delete ( rwtxn, outpoint_key) ?)
266+ }
267+
187268 pub fn withdrawal_bundle_event_blocks (
188269 & self ,
189270 ) -> & RoDatabaseUnique <
@@ -222,13 +303,23 @@ impl State {
222303 & self ,
223304 rotxn : & RoTxn ,
224305 addresses : & HashSet < Address > ,
306+ height_threshold : u32 ,
225307 ) -> Result < HashMap < OutPoint , FilledOutput > , Error > {
226- let utxos: HashMap < OutPoint , FilledOutput > = self
227- . utxos
228- . iter ( rotxn) ?
229- . filter ( |( _, output) | Ok ( addresses. contains ( & output. address ) ) )
230- . map ( |( key, output) | Ok ( ( key. to_outpoint ( ) , output) ) )
231- . collect ( ) ?;
308+ let mut utxos = HashMap :: new ( ) ;
309+ for address in addresses {
310+ let start = AddressOutPointKey :: start ( * address) ;
311+ let end = AddressOutPointKey :: end ( * address) ;
312+ let mut iter = self
313+ . utxos_by_address
314+ . range ( rotxn, & ( start..=end) )
315+ . map_err ( sneed:: db:: Error :: from) ?;
316+ while let Some ( ( key, entry) ) = iter. next ( ) ? {
317+ if entry. created_height >= height_threshold {
318+ let outpoint = key. outpoint_key ( ) . to_outpoint ( ) ;
319+ utxos. insert ( outpoint, entry. output ) ;
320+ }
321+ }
322+ }
232323 Ok ( utxos)
233324 }
234325
@@ -300,13 +391,13 @@ impl State {
300391 . try_get ( rotxn, & key) ?
301392 . ok_or ( Error :: NoStxo { outpoint : * input } ) ?;
302393 assert_eq ! (
303- stxo. inpoint,
394+ stxo. output . inpoint,
304395 InPoint :: Regular {
305396 txid,
306397 vin: vin as u32
307398 }
308399 ) ;
309- spent_utxos. push ( stxo. output ) ;
400+ spent_utxos. push ( stxo. output . output ) ;
310401 }
311402 spent_utxos. reverse ( ) ;
312403 Ok ( FilledTransaction {
@@ -656,7 +747,8 @@ impl State {
656747 let mut total_deposit_stxo_value = bitcoin:: Amount :: ZERO ;
657748 let mut total_withdrawal_stxo_value = bitcoin:: Amount :: ZERO ;
658749 self . stxos . iter ( rotxn) ?. map_err ( Error :: from) . for_each (
659- |( outpoint_key, spent_output) | {
750+ |( outpoint_key, spent_entry) | {
751+ let spent_output = spent_entry. output ;
660752 let outpoint = outpoint_key. to_outpoint ( ) ;
661753 if let OutPoint :: Deposit ( _) = outpoint {
662754 total_deposit_stxo_value = total_deposit_stxo_value
0 commit comments