@@ -2,7 +2,6 @@ package accounter
22
33import (
44 "encoding/hex"
5- "fmt"
65 "log"
76 "sync"
87 "time"
@@ -28,8 +27,10 @@ type Accounter struct {
2827 xpubs []string
2928 blockHeight uint32 // height at which we want to compute the balance
3029
31- addresses map [string ]address // map of address script => (Address, txHashes)
32- transactions map [string ]transaction // map of txhash => transaction
30+ addresses map [string ]address // map of address script => (Address, txHashes)
31+ txAddressesMu sync.Mutex
32+ txAddresses map [string ][]* deriver.Address // map of txhash => []Address
33+ transactions map [string ]transaction // map of txhash => transaction
3334
3435 backend backend.Backend
3536 deriver * deriver.AddressDeriver
@@ -71,20 +72,19 @@ type vout struct {
7172}
7273
7374// New instantiates a new Accounter.
74- // TODO: find a better way to pass options to the NewCounter. Maybe thru a config or functional option params?
7575func New (b backend.Backend , addressDeriver * deriver.AddressDeriver , lookahead uint32 , blockHeight uint32 ) * Accounter {
76- a := & Accounter {
76+ return & Accounter {
7777 blockHeight : blockHeight ,
7878 backend : b ,
7979 deriver : addressDeriver ,
8080 lookahead : lookahead ,
8181 lastAddresses : [2 ]uint32 {lookahead , lookahead },
82+ addresses : make (map [string ]address ),
83+ txAddresses : make (map [string ][]* deriver.Address ),
84+ transactions : make (map [string ]transaction ),
85+ addrResponses : b .AddrResponses (),
86+ txResponses : b .TxResponses (),
8287 }
83- a .addresses = make (map [string ]address )
84- a .transactions = make (map [string ]transaction )
85- a .addrResponses = b .AddrResponses ()
86- a .txResponses = b .TxResponses ()
87- return a
8888}
8989
9090func (a * Accounter ) ComputeBalance () uint64 {
@@ -114,31 +114,25 @@ func (a *Accounter) fetchTransactions() {
114114func (a * Accounter ) processTransactions () {
115115 for hash , tx := range a .transactions {
116116 // remove transactions which are too recent
117- if tx .height > int64 (a .blockHeight ) {
118- reporter . GetInstance (). Logf ( "transaction %s has height %d > BLOCK HEIGHT ( %d)" , hash , tx .height , a .blockHeight )
117+ if ( tx .height > int64 (a .blockHeight )) || ( tx . height == 0 ) {
118+ log . Printf ( "backend failed to filter tx %s (%d, %d)" , hash , tx .height , a .blockHeight )
119119 delete (a .transactions , hash )
120120 }
121- // remove transactions which haven't been mined
122- if tx .height <= 0 {
123- reporter .GetInstance ().Logf ("transaction %s has not been mined, yet (height=%d)" , hash , tx .height )
124- delete (a .transactions , hash )
121+ if tx .height < 0 {
122+ log .Panicf ("tx %s has negative height %d" , hash , tx .height )
125123 }
126124 }
127- reporter .GetInstance ().SetTxAfterFilter (int32 (len (a .transactions )))
128- reporter .GetInstance ().Log ("done filtering" )
129125
130126 // TODO: we could check that scheduled == fetched in the metrics we track in reporter.
131-
132127 // parse the transaction hex
133128 for hash , tx := range a .transactions {
134129 b , err := hex .DecodeString (tx .hex )
135130 if err != nil {
136- fmt . Printf ("failed to unhex transaction %s: %s" , hash , tx .hex )
131+ log . Panicf ("failed to unhex transaction %s: %s" , hash , tx .hex )
137132 }
138133 parsedTx , err := btcutil .NewTxFromBytes (b )
139134 if err != nil {
140- fmt .Printf ("failed to parse transaction %s: %s" , hash , tx .hex )
141- continue
135+ log .Panicf ("failed to parse transaction %s: %s" , hash , tx .hex )
142136 }
143137 for _ , txin := range parsedTx .MsgTx ().TxIn {
144138 tx .vin = append (tx .vin , vin {
@@ -234,7 +228,10 @@ func (a *Accounter) sendWork() {
234228 indexes [change ]++
235229 }
236230 }
237- // apparently no more work for us, so we can sleep a bit
231+ // apparently no more work for now.
232+
233+ // TODO: we should either merge sendWork/recvWork or use some kind of mutex to sleep exactly
234+ // until there's more work that needs to be done. For now, a simple sleep works.
238235 time .Sleep (time .Millisecond * 100 )
239236 }
240237}
@@ -251,6 +248,7 @@ func (a *Accounter) recvWork() {
251248 continue
252249 }
253250 reporter .GetInstance ().IncAddressesFetched ()
251+ reporter .GetInstance ().Logf ("received address: %s" , resp .Address )
254252
255253 a .countMu .Lock ()
256254 a .processedAddrCount ++
@@ -263,20 +261,23 @@ func (a *Accounter) recvWork() {
263261
264262 a .countMu .Lock ()
265263 for _ , txHash := range resp .TxHashes {
264+ // TODO: mark this txHash as having been scheduled. So we don't fetch it multiple times.
266265 if _ , exists := a .transactions [txHash ]; ! exists {
267266 a .backend .TxRequest (txHash )
268267 a .seenTxCount ++
269268 }
270269 }
271270 a .countMu .Unlock ()
272271
272+ // we can only update the lastAddresses after we filter the transaction heights
273+ a .txAddressesMu .Lock ()
274+ for _ , txHash := range resp .TxHashes {
275+ a .txAddresses [txHash ] = append (a .txAddresses [txHash ], resp .Address )
276+ }
277+ a .txAddressesMu .Unlock ()
278+
273279 reporter .GetInstance ().Logf ("address %s has %d transactions" , resp .Address , len (resp .TxHashes ))
274280
275- if resp .HasTransactions () {
276- a .countMu .Lock ()
277- a .lastAddresses [resp .Address .Change ()] = Max (a .lastAddresses [resp .Address .Change ()], resp .Address .Index ()+ a .lookahead )
278- a .countMu .Unlock ()
279- }
280281 case resp , ok := <- txResponses :
281282 // channel is closed now, so ignore this case by blocking forever
282283 if ! ok {
@@ -285,6 +286,7 @@ func (a *Accounter) recvWork() {
285286 }
286287
287288 reporter .GetInstance ().IncTxFetched ()
289+ reporter .GetInstance ().Logf ("received tx: %s" , resp .Hash )
288290
289291 a .countMu .Lock ()
290292 a .processedTxCount ++
@@ -297,6 +299,15 @@ func (a *Accounter) recvWork() {
297299 vout : []vout {},
298300 }
299301 a .transactions [resp .Hash ] = tx
302+
303+ a .txAddressesMu .Lock ()
304+ a .countMu .Lock ()
305+ for _ , addr := range a .txAddresses [resp .Hash ] {
306+ a .lastAddresses [addr .Change ()] = Max (a .lastAddresses [addr .Change ()], addr .Index ()+ a .lookahead )
307+ }
308+ a .countMu .Unlock ()
309+ a .txAddressesMu .Unlock ()
310+
300311 case <- time .Tick (1 * time .Second ):
301312 if a .complete () {
302313 return
0 commit comments