• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 16181619122

09 Jul 2025 10:33PM UTC coverage: 55.326% (-2.3%) from 57.611%
16181619122

Pull #10060

github

web-flow
Merge d15e8671f into 0e830da9d
Pull Request #10060: sweep: fix expected spending events being missed

9 of 26 new or added lines in 2 files covered. (34.62%)

23695 existing lines in 280 files now uncovered.

108518 of 196143 relevant lines covered (55.33%)

22354.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/watchtower/standalone.go
1
package watchtower
2

3
import (
4
        "net"
5
        "sync/atomic"
6
        "time"
7

8
        "github.com/btcsuite/btcd/btcec/v2"
9
        "github.com/lightningnetwork/lnd/brontide"
10
        "github.com/lightningnetwork/lnd/lnencrypt"
11
        "github.com/lightningnetwork/lnd/tor"
12
        "github.com/lightningnetwork/lnd/watchtower/lookout"
13
        "github.com/lightningnetwork/lnd/watchtower/wtserver"
14
)
15

16
// Standalone encapsulates the server-side functionality required by watchtower
17
// clients. A Standalone couples the two primary subsystems such that, as a
18
// unit, this instance can negotiate sessions with clients, accept state updates
19
// for active sessions, monitor the chain for breaches matching known breach
20
// hints, publish reconstructed justice transactions on behalf of tower clients.
21
type Standalone struct {
22
        started uint32 // to be used atomically
23
        stopped uint32 // to be used atomically
24

25
        cfg *Config
26

27
        // listeners is a reference to the wtserver's listeners.
28
        listeners []net.Listener
29

30
        // server is the client endpoint, used for negotiating sessions and
31
        // uploading state updates.
32
        server wtserver.Interface
33

34
        // lookout is a service that monitors the chain and inspects the
35
        // transactions found in new blocks against the state updates received
36
        // by the server.
37
        lookout lookout.Service
38
}
39

40
// New validates the passed Config and returns a fresh Standalone instance if
41
// the tower's subsystems could be properly initialized.
UNCOV
42
func New(cfg *Config) (*Standalone, error) {
×
UNCOV
43
        // The tower must have listening address in order to accept new updates
×
UNCOV
44
        // from clients.
×
UNCOV
45
        if len(cfg.ListenAddrs) == 0 {
×
46
                return nil, ErrNoListeners
×
47
        }
×
48

49
        // Assign the default read timeout if none is provided.
UNCOV
50
        if cfg.ReadTimeout == 0 {
×
51
                cfg.ReadTimeout = DefaultReadTimeout
×
52
        }
×
53

54
        // Assign the default write timeout if none is provided.
UNCOV
55
        if cfg.WriteTimeout == 0 {
×
56
                cfg.WriteTimeout = DefaultWriteTimeout
×
57
        }
×
58

UNCOV
59
        punisher := lookout.NewBreachPunisher(&lookout.PunisherConfig{
×
UNCOV
60
                PublishTx: cfg.PublishTx,
×
UNCOV
61
        })
×
UNCOV
62

×
UNCOV
63
        // Initialize the lookout service with its required resources.
×
UNCOV
64
        lookout := lookout.New(&lookout.Config{
×
UNCOV
65
                BlockFetcher:   cfg.BlockFetcher,
×
UNCOV
66
                DB:             cfg.DB,
×
UNCOV
67
                EpochRegistrar: cfg.EpochRegistrar,
×
UNCOV
68
                Punisher:       punisher,
×
UNCOV
69
                MinBackoff:     time.Second,
×
UNCOV
70
                MaxBackoff:     time.Minute,
×
UNCOV
71
                MaxNumRetries:  5,
×
UNCOV
72
        })
×
UNCOV
73

×
UNCOV
74
        // Create a brontide listener on each of the provided listening
×
UNCOV
75
        // addresses. Client should be able to connect to any of open ports to
×
UNCOV
76
        // communicate with this Standalone instance.
×
UNCOV
77
        listeners := make([]net.Listener, 0, len(cfg.ListenAddrs))
×
UNCOV
78
        for _, listenAddr := range cfg.ListenAddrs {
×
UNCOV
79
                listener, err := brontide.NewListener(
×
UNCOV
80
                        cfg.NodeKeyECDH, listenAddr.String(),
×
UNCOV
81
                        brontide.DisabledBanClosure,
×
UNCOV
82
                )
×
UNCOV
83
                if err != nil {
×
84
                        return nil, err
×
85
                }
×
86

UNCOV
87
                listeners = append(listeners, listener)
×
88
        }
89

90
        // Initialize the server with its required resources.
UNCOV
91
        server, err := wtserver.New(&wtserver.Config{
×
UNCOV
92
                ChainHash:     cfg.ChainHash,
×
UNCOV
93
                DB:            cfg.DB,
×
UNCOV
94
                NodeKeyECDH:   cfg.NodeKeyECDH,
×
UNCOV
95
                Listeners:     listeners,
×
UNCOV
96
                ReadTimeout:   cfg.ReadTimeout,
×
UNCOV
97
                WriteTimeout:  cfg.WriteTimeout,
×
UNCOV
98
                NewAddress:    cfg.NewAddress,
×
UNCOV
99
                DisableReward: true,
×
UNCOV
100
        })
×
UNCOV
101
        if err != nil {
×
102
                return nil, err
×
103
        }
×
104

UNCOV
105
        return &Standalone{
×
UNCOV
106
                cfg:       cfg,
×
UNCOV
107
                listeners: listeners,
×
UNCOV
108
                server:    server,
×
UNCOV
109
                lookout:   lookout,
×
UNCOV
110
        }, nil
×
111
}
112

113
// Start idempotently starts the Standalone, an error is returned if the
114
// subsystems could not be initialized.
UNCOV
115
func (w *Standalone) Start() error {
×
UNCOV
116
        if !atomic.CompareAndSwapUint32(&w.started, 0, 1) {
×
117
                return nil
×
118
        }
×
119

UNCOV
120
        log.Infof("Starting watchtower")
×
UNCOV
121

×
UNCOV
122
        // If a tor controller exists in the config, then automatically create a
×
UNCOV
123
        // hidden service for the watchtower to accept inbound connections from.
×
UNCOV
124
        if w.cfg.TorController != nil {
×
125
                log.Infof("Creating watchtower hidden service")
×
126
                if err := w.createNewHiddenService(); err != nil {
×
127
                        return err
×
128
                }
×
129
        }
130

UNCOV
131
        if err := w.lookout.Start(); err != nil {
×
132
                return err
×
133
        }
×
UNCOV
134
        if err := w.server.Start(); err != nil {
×
135
                w.lookout.Stop()
×
136
                return err
×
137
        }
×
138

UNCOV
139
        log.Infof("Watchtower started successfully")
×
UNCOV
140

×
UNCOV
141
        return nil
×
142
}
143

144
// Stop idempotently stops the Standalone and blocks until the subsystems have
145
// completed their shutdown.
UNCOV
146
func (w *Standalone) Stop() error {
×
UNCOV
147
        if !atomic.CompareAndSwapUint32(&w.stopped, 0, 1) {
×
148
                return nil
×
149
        }
×
150

UNCOV
151
        log.Infof("Stopping watchtower")
×
UNCOV
152

×
UNCOV
153
        w.server.Stop()
×
UNCOV
154
        w.lookout.Stop()
×
UNCOV
155

×
UNCOV
156
        log.Infof("Watchtower stopped successfully")
×
UNCOV
157

×
UNCOV
158
        return nil
×
159
}
160

161
// createNewHiddenService automatically sets up a v2 or v3 onion service in
162
// order to listen for inbound connections over Tor.
163
func (w *Standalone) createNewHiddenService() error {
×
164
        // Get all the ports the watchtower is listening on. These will be used to
×
165
        // map the hidden service's virtual port.
×
166
        listenPorts := make([]int, 0, len(w.listeners))
×
167
        for _, listener := range w.listeners {
×
168
                port := listener.Addr().(*net.TCPAddr).Port
×
169
                listenPorts = append(listenPorts, port)
×
170
        }
×
171

172
        encrypter, err := lnencrypt.KeyRingEncrypter(w.cfg.KeyRing)
×
173
        if err != nil {
×
174
                return err
×
175
        }
×
176

177
        // Once we've created the port mapping, we can automatically create the
178
        // hidden service. The service's private key will be saved on disk in order
179
        // to persistently have access to this hidden service across restarts.
180
        onionCfg := tor.AddOnionConfig{
×
181
                VirtualPort: DefaultPeerPort,
×
182
                TargetPorts: listenPorts,
×
183
                Store: tor.NewOnionFile(
×
184
                        w.cfg.WatchtowerKeyPath, 0600, w.cfg.EncryptKey,
×
185
                        encrypter,
×
186
                ),
×
187
                Type: w.cfg.Type,
×
188
        }
×
189

×
190
        addr, err := w.cfg.TorController.AddOnion(onionCfg)
×
191
        if err != nil {
×
192
                return err
×
193
        }
×
194

195
        // Append this address to ExternalIPs so that it will be exposed in
196
        // tower info calls.
197
        w.cfg.ExternalIPs = append(w.cfg.ExternalIPs, addr)
×
198

×
199
        return nil
×
200
}
201

202
// PubKey returns the public key for the watchtower used to authentication and
203
// encrypt traffic with clients.
204
//
205
// NOTE: Part of the watchtowerrpc.WatchtowerBackend interface.
UNCOV
206
func (w *Standalone) PubKey() *btcec.PublicKey {
×
UNCOV
207
        return w.cfg.NodeKeyECDH.PubKey()
×
UNCOV
208
}
×
209

210
// ListeningAddrs returns the listening addresses where the watchtower server
211
// can accept client connections.
212
//
213
// NOTE: Part of the watchtowerrpc.WatchtowerBackend interface.
UNCOV
214
func (w *Standalone) ListeningAddrs() []net.Addr {
×
UNCOV
215
        addrs := make([]net.Addr, 0, len(w.listeners))
×
UNCOV
216
        for _, listener := range w.listeners {
×
UNCOV
217
                addrs = append(addrs, listener.Addr())
×
UNCOV
218
        }
×
219

UNCOV
220
        return addrs
×
221
}
222

223
// ExternalIPs returns the addresses where the watchtower can be reached by
224
// clients externally.
225
//
226
// NOTE: Part of the watchtowerrpc.WatchtowerBackend interface.
UNCOV
227
func (w *Standalone) ExternalIPs() []net.Addr {
×
UNCOV
228
        addrs := make([]net.Addr, 0, len(w.cfg.ExternalIPs))
×
UNCOV
229
        addrs = append(addrs, w.cfg.ExternalIPs...)
×
UNCOV
230

×
UNCOV
231
        return addrs
×
UNCOV
232
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc