@@ -69,6 +69,7 @@ type xpubData struct {
6969 txCountEstimate uint32
7070 sentSat big.Int
7171 balanceSat big.Int
72+ mergedTxids xpubTxids
7273 addresses [][]xpubAddress
7374}
7475
@@ -197,10 +198,32 @@ func (w *Worker) xpubGetAddressTxids(addrDesc bchain.AddressDescriptor, mempool
197198 return txs , complete , nil
198199}
199200
200- func (w * Worker ) xpubCheckAndLoadTxids (ad * xpubAddress , filter * AddressFilter , maxHeight uint32 , maxResults int ) error {
201+ func isUnfilteredXpubTxidFilter (filter * AddressFilter ) bool {
202+ return filter == nil || filter .FromHeight == 0 && filter .ToHeight == 0 && filter .Vout == AddressFilterVoutOff
203+ }
204+
205+ func mergeXpubTxids (data * xpubData ) xpubTxids {
206+ txcMap := make (map [string ]struct {}, data .txCountEstimate )
207+ txc := make (xpubTxids , 0 , data .txCountEstimate )
208+ for _ , da := range data .addresses {
209+ for i := range da {
210+ for _ , txid := range da [i ].txids {
211+ if _ , foundTx := txcMap [txid .txid ]; foundTx {
212+ continue
213+ }
214+ txcMap [txid .txid ] = struct {}{}
215+ txc = append (txc , txid )
216+ }
217+ }
218+ }
219+ sort .Stable (txc )
220+ return txc
221+ }
222+
223+ func (w * Worker ) xpubCheckAndLoadTxids (ad * xpubAddress , maxHeight uint32 , maxResults int ) (bool , error ) {
201224 // skip if not used
202225 if ad .balance == nil {
203- return nil
226+ return false , nil
204227 }
205228 // if completely loaded, check if there are not some new txs and load if necessary
206229 if ad .complete {
@@ -214,14 +237,14 @@ func (w *Worker) xpubCheckAndLoadTxids(ad *xpubAddress, filter *AddressFilter, m
214237 glog .Warning ("xpubCheckAndLoadTxids inconsistency " , ad .addrDesc , ", ad.txs=" , ad .txs , ", ad.balance.Txs=" , ad .balance .Txs )
215238 }
216239 }
217- return err
240+ return err == nil , err
218241 }
219- return nil
242+ return false , nil
220243 }
221244 // load all txids to get paging correctly
222245 newTxids , complete , err := w .xpubGetAddressTxids (ad .addrDesc , false , 0 , maxHeight , maxInt )
223246 if err != nil {
224- return err
247+ return false , err
225248 }
226249 ad .txids = newTxids
227250 ad .complete = complete
@@ -232,7 +255,7 @@ func (w *Worker) xpubCheckAndLoadTxids(ad *xpubAddress, filter *AddressFilter, m
232255 glog .Warning ("xpubCheckAndLoadTxids inconsistency " , ad .addrDesc , ", ad.txs=" , ad .txs , ", ad.balance.Txs=" , ad .balance .Txs )
233256 }
234257 }
235- return nil
258+ return true , nil
236259}
237260
238261func (w * Worker ) xpubDerivedAddressBalance (data * xpubData , ad * xpubAddress ) (bool , error ) {
@@ -420,6 +443,7 @@ func (w *Worker) getXpubData(xd *bchain.XpubDescriptor, page int, txsOnPage int,
420443 data .balanceSat = * new (big.Int )
421444 data .sentSat = * new (big.Int )
422445 data .txCountEstimate = 0
446+ data .mergedTxids = nil
423447 var minDerivedIndex int
424448 totalDerived := 0
425449 for i , change := range xd .ChangeIndexes {
@@ -431,13 +455,22 @@ func (w *Worker) getXpubData(xd *bchain.XpubDescriptor, page int, txsOnPage int,
431455 }
432456 }
433457 if option >= AccountDetailsTxidHistory {
458+ txidsChanged := false
434459 for _ , da := range data .addresses {
435460 for i := range da {
436- if err = w .xpubCheckAndLoadTxids (& da [i ], filter , bestheight , (page + 1 )* txsOnPage ); err != nil {
461+ changed := false
462+ if changed , err = w .xpubCheckAndLoadTxids (& da [i ], bestheight , (page + 1 )* txsOnPage ); err != nil {
437463 return nil , 0 , inCache , err
438464 }
465+ txidsChanged = txidsChanged || changed
439466 }
440467 }
468+ if txidsChanged {
469+ data .mergedTxids = nil
470+ }
471+ if isUnfilteredXpubTxidFilter (filter ) && data .mergedTxids == nil {
472+ data .mergedTxids = mergeXpubTxids (& data )
473+ }
441474 }
442475 }
443476 data .accessed = time .Now ().Unix ()
@@ -564,30 +597,35 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
564597 }
565598 }
566599 if option >= AccountDetailsTxidHistory {
567- txcMap := make (map [string ]bool )
568- txc = make (xpubTxids , 0 , 32 )
569- for _ , da := range data .addresses {
570- for i := range da {
571- ad := & da [i ]
572- for _ , txid := range ad .txids {
573- added , foundTx := txcMap [txid .txid ]
574- // count txs regardless of filter but only once
575- if ! foundTx {
576- txCount ++
577- }
578- // add tx only once
579- if ! added {
580- add := txidFilter == nil || txidFilter (& txid , ad )
581- txcMap [txid .txid ] = add
582- if add {
583- txc = append (txc , txid )
600+ if txidFilter == nil {
601+ // Shared with xpubData cache; do not mutate in this request path.
602+ txc = data .mergedTxids
603+ if txc == nil {
604+ txc = mergeXpubTxids (data )
605+ }
606+ txCount = len (txc )
607+ } else {
608+ txcMap := make (map [string ]bool )
609+ txc = make (xpubTxids , 0 , 32 )
610+ for _ , da := range data .addresses {
611+ for i := range da {
612+ ad := & da [i ]
613+ for _ , txid := range ad .txids {
614+ added := txcMap [txid .txid ]
615+ // add tx only once
616+ if ! added {
617+ add := txidFilter (& txid , ad )
618+ txcMap [txid .txid ] = add
619+ if add {
620+ txc = append (txc , txid )
621+ }
584622 }
585623 }
586624 }
587625 }
626+ sort .Stable (txc )
627+ txCount = len (txcMap )
588628 }
589- sort .Stable (txc )
590- txCount = len (txcMap )
591629 totalResults := txCount
592630 if filtered {
593631 totalResults = - 1
@@ -692,7 +730,7 @@ func (w *Worker) GetXpubUtxo(xpub string, onlyConfirmed bool, gap int) (Utxos, e
692730 if err != nil {
693731 return nil , err
694732 }
695- data , _ , inCache , err := w .getXpubData (xd , 0 , 1 , AccountDetailsBasic , & AddressFilter {
733+ data , bestheight , inCache , err := w .getXpubData (xd , 0 , 1 , AccountDetailsBasic , & AddressFilter {
696734 Vout : AddressFilterVoutOff ,
697735 OnlyConfirmed : onlyConfirmed ,
698736 }, gap )
@@ -710,7 +748,7 @@ func (w *Worker) GetXpubUtxo(xpub string, onlyConfirmed bool, gap int) (Utxos, e
710748 }
711749 onlyMempool = true
712750 }
713- utxos , err := w .getAddrDescUtxo (ad .addrDesc , ad .balance , onlyConfirmed , onlyMempool )
751+ utxos , err := w .getAddrDescUtxo (ad .addrDesc , ad .balance , onlyConfirmed , onlyMempool , & bestheight )
714752 if err != nil {
715753 return nil , err
716754 }
0 commit comments