@@ -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 {
@@ -112,33 +112,17 @@ func (a *Accounter) fetchTransactions() {
112112}
113113
114114func (a * Accounter ) processTransactions () {
115- for hash , tx := range a .transactions {
116- // 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 )
119- delete (a .transactions , hash )
120- }
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 )
125- }
126- }
127- reporter .GetInstance ().SetTxAfterFilter (int32 (len (a .transactions )))
128- reporter .GetInstance ().Log ("done filtering" )
129-
130115 // TODO: we could check that scheduled == fetched in the metrics we track in reporter.
131116
132117 // parse the transaction hex
133118 for hash , tx := range a .transactions {
134119 b , err := hex .DecodeString (tx .hex )
135120 if err != nil {
136- fmt . Printf ("failed to unhex transaction %s: %s" , hash , tx .hex )
121+ log . Panicf ("failed to unhex transaction %s: %s" , hash , tx .hex )
137122 }
138123 parsedTx , err := btcutil .NewTxFromBytes (b )
139124 if err != nil {
140- fmt .Printf ("failed to parse transaction %s: %s" , hash , tx .hex )
141- continue
125+ log .Panicf ("failed to parse transaction %s: %s" , hash , tx .hex )
142126 }
143127 for _ , txin := range parsedTx .MsgTx ().TxIn {
144128 tx .vin = append (tx .vin , vin {
@@ -234,7 +218,10 @@ func (a *Accounter) sendWork() {
234218 indexes [change ]++
235219 }
236220 }
237- // apparently no more work for us, so we can sleep a bit
221+ // apparently no more work for now.
222+
223+ // TODO: we should either merge sendWork/recvWork or use some kind of mutex to sleep exactly
224+ // until there's more work that needs to be done. For now, a simple sleep works.
238225 time .Sleep (time .Millisecond * 100 )
239226 }
240227}
@@ -251,6 +238,7 @@ func (a *Accounter) recvWork() {
251238 continue
252239 }
253240 reporter .GetInstance ().IncAddressesFetched ()
241+ reporter .GetInstance ().Logf ("received address: %s" , resp .Address )
254242
255243 a .countMu .Lock ()
256244 a .processedAddrCount ++
@@ -270,13 +258,15 @@ func (a *Accounter) recvWork() {
270258 }
271259 a .countMu .Unlock ()
272260
261+ // we can only update the lastAddresses after we filter the transaction heights
262+ a .txAddressesMu .Lock ()
263+ for _ , txHash := range resp .TxHashes {
264+ a .txAddresses [txHash ] = append (a .txAddresses [txHash ], resp .Address )
265+ }
266+ a .txAddressesMu .Unlock ()
267+
273268 reporter .GetInstance ().Logf ("address %s has %d transactions" , resp .Address , len (resp .TxHashes ))
274269
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- }
280270 case resp , ok := <- txResponses :
281271 // channel is closed now, so ignore this case by blocking forever
282272 if ! ok {
@@ -285,18 +275,38 @@ func (a *Accounter) recvWork() {
285275 }
286276
287277 reporter .GetInstance ().IncTxFetched ()
278+ reporter .GetInstance ().Logf ("received tx: %s" , resp .Hash )
288279
289280 a .countMu .Lock ()
290281 a .processedTxCount ++
291282 a .countMu .Unlock ()
292283
284+ if resp .Height > int64 (a .blockHeight ) {
285+ continue
286+ }
287+ if resp .Height == 0 {
288+ continue
289+ }
290+ if resp .Height < 0 {
291+ log .Panicf ("tx %s has negative height %d" , resp .Hash , resp .Height )
292+ }
293+
293294 tx := transaction {
294295 height : resp .Height ,
295296 hex : resp .Hex ,
296297 vin : []vin {},
297298 vout : []vout {},
298299 }
299300 a .transactions [resp .Hash ] = tx
301+
302+ a .txAddressesMu .Lock ()
303+ a .countMu .Lock ()
304+ for _ , addr := range a .txAddresses [resp .Hash ] {
305+ a .lastAddresses [addr .Change ()] = Max (a .lastAddresses [addr .Change ()], addr .Index ()+ a .lookahead )
306+ }
307+ a .countMu .Unlock ()
308+ a .txAddressesMu .Unlock ()
309+
300310 case <- time .Tick (1 * time .Second ):
301311 if a .complete () {
302312 return
0 commit comments