1313from collections import defaultdict
1414from typing import TYPE_CHECKING , Type , Optional , Dict , Sequence , Tuple , List
1515import itertools
16+ from functools import partial
1617
1718from aiorpcx import run_in_thread
1819
@@ -323,6 +324,7 @@ def get_txnums(
323324 it .seek (prefix + pack_txnum (txnum_min ))
324325 txnum_min = txnum_min if txnum_min is not None else 0
325326 txnum_max = txnum_max if txnum_max is not None else float ('inf' )
327+ assert txnum_min <= txnum_max , f"txnum_min={ txnum_min } , txnum_max={ txnum_max } "
326328 for db_key , db_val in it :
327329 tx_numb = db_key [- TXNUM_LEN :]
328330 if limit == 0 :
@@ -359,29 +361,46 @@ def get_spender_txnum_for_txo(self, prev_txnum: int, txout_idx: int) -> Optional
359361 spender_txnum = unpack_txnum (spender_txnumb )
360362 return spender_txnum
361363
362- def fs_get_intermediate_statushash_for_hashx (self , hashX : bytes ) -> Tuple [int , bytes ]:
364+ def fs_get_intermediate_statushash_for_hashx (
365+ self ,
366+ * ,
367+ hashX : bytes ,
368+ txnum_max : int = None ,
369+ ) -> Tuple [int , bytes ]:
363370 '''For a hashX, returns (tx_num, status), with the latest stored statushash
364- and corresponding tx_num.
371+ and corresponding tx_num, where tx_num < txnum_max .
365372 This can be used to efficiently calculate the status of a hashX as
366373 only the txs mined after(>) tx_num will need to be hashed.
367374 '''
375+ # first, search in-memory, among the unflushed statuses
368376 unflushed_statushashes = self ._unflushed_hashx_to_statushash .get (hashX , [])
369377 if len (unflushed_statushashes ) > 0 :
370- tx_num , status = unflushed_statushashes [- 1 ]
378+ for tx_num , status in reversed (unflushed_statushashes ):
379+ if txnum_max is None or tx_num < txnum_max :
380+ return tx_num , status
381+ # second, search in the on-disk DB
382+ prefix = b'S' + hashX
383+ it = self .db .iterator (prefix = prefix , reverse = True )
384+ if txnum_max is not None :
385+ it .seek (prefix + pack_txnum (txnum_max ))
386+ for db_key , db_val in it :
387+ tx_numb = db_key [- TXNUM_LEN :]
388+ tx_num = unpack_txnum (tx_numb )
389+ status = db_val
390+ break
371391 else :
372- prefix = b'S' + hashX
373- for db_key , db_val in self .db .iterator (prefix = prefix , reverse = True ):
374- tx_numb = db_key [- TXNUM_LEN :]
375- tx_num = unpack_txnum (tx_numb )
376- status = db_val
377- break
378- else :
379- tx_num = - 1
380- status = bytes (32 )
392+ tx_num = 0
393+ status = bytes (32 )
381394 return tx_num , status
382395
383- async def get_intermediate_statushash_for_hashx (self , hashX : bytes ) -> Tuple [int , bytes ]:
384- return await run_in_thread (self .fs_get_intermediate_statushash_for_hashx , hashX )
396+ async def get_intermediate_statushash_for_hashx (
397+ self ,
398+ * ,
399+ hashX : bytes ,
400+ txnum_max : int = None ,
401+ ) -> Tuple [int , bytes ]:
402+ f = partial (self .fs_get_intermediate_statushash_for_hashx , hashX = hashX , txnum_max = txnum_max )
403+ return await run_in_thread (f )
385404
386405 def store_intermediate_statushash_for_hashx (
387406 self ,
@@ -397,6 +416,7 @@ def store_intermediate_statushash_for_hashx(
397416 '''
398417 if hashX not in self ._unflushed_hashx_to_statushash :
399418 self ._unflushed_hashx_to_statushash [hashX ] = []
419+ # maintain invariant that unflushed statuses are in order (increasing tx_num):
400420 if len (self ._unflushed_hashx_to_statushash [hashX ]) > 0 :
401421 tx_num_last , status_last = self ._unflushed_hashx_to_statushash [hashX ][- 1 ]
402422 if tx_num <= tx_num_last :
0 commit comments