-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathwatcher.go
147 lines (124 loc) · 2.75 KB
/
watcher.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package bitcoinwalletwatcher
import (
"context"
"errors"
"log"
"time"
"github.com/blockcypher/gobcy"
)
// Watcher struct
type Watcher struct {
info *InfoFile
loopInSec time.Duration
addresses []string
callbacks []WatcherCallback
api gobcy.API
}
// WatcherCallback method
type WatcherCallback func(string, int)
var (
// ErrMissingBlockCyperToken error
ErrMissingBlockCyperToken = errors.New("blockcyper.io API token is not provied")
)
// NewWatcher returns new bitcoin wallet watcher
func NewWatcher(cfg *Config) (*Watcher, error) {
if cfg.BlockCyperToken == "" {
return nil, ErrMissingBlockCyperToken
}
info, err := NewInfoStorage(cfg.InfoFile)
if err != nil {
return nil, err
}
if cfg.Coin == "" {
cfg.Coin = "btc"
}
if cfg.Chain == "" {
cfg.Coin = "main"
}
api := gobcy.API{
Coin: cfg.Coin,
Chain: cfg.Chain,
Token: cfg.BlockCyperToken,
}
return &Watcher{
info: info,
loopInSec: time.Duration(cfg.DefaultLoopSec),
addresses: cfg.Adresses,
api: api,
}, nil
}
// SetCallback sets a new callback
func (w *Watcher) SetCallback(callback WatcherCallback) {
w.callbacks = append(w.callbacks, callback)
}
// AddNewAddress adds new address to the list in order to listen it
func (w *Watcher) AddNewAddress(address string) {
w.addresses = append(w.addresses, address)
}
// IsAddressExists checks the address list if the given address exists
func (w *Watcher) IsAddressExists(address string) bool {
for _, addr := range w.addresses {
if addr == address {
return true
}
}
return false
}
// RemoveAddress removes the given address from the list
func (w *Watcher) RemoveAddress(address string) {
var temp []string
index := 0
for _, addr := range w.addresses {
if addr != address {
temp[index] = addr
index++
}
}
w.addresses = temp
}
// Run runs the watcher
func (w *Watcher) Run(ctx context.Context) {
for {
select {
case <-ctx.Done():
w.Stop()
return
default:
w.readBlock()
time.Sleep(time.Second * w.loopInSec)
}
}
}
func (w *Watcher) readBlock() {
block, err := w.api.GetBlock(w.info.CurrentBlock, "", nil)
if err != nil {
log.Printf("Error getting block: %v\n", err)
return
}
for _, tx := range block.TXids {
w.checkTx(tx)
}
w.info.Update(block.Height + 1)
}
func (w *Watcher) checkTx(txHash string) {
tx, err := w.api.GetTX(txHash, nil)
if err != nil {
log.Printf("Error getting transaction %v\n", err)
return
}
for _, out := range tx.Outputs {
for _, outAddress := range out.Addresses {
if contains(w.addresses, outAddress) {
for _, c := range w.callbacks {
c(outAddress, out.Value)
}
}
}
}
}
// Stop runs when watcher stopping
func (w *Watcher) Stop() {
if err := w.info.Save(); err != nil {
log.Fatalf("error saving file %v", err)
}
}