You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
218 lines
6.2 KiB
218 lines
6.2 KiB
package watchtower |
|
|
|
import ( |
|
"net" |
|
"sync/atomic" |
|
|
|
"github.com/btcsuite/btcd/btcec" |
|
"github.com/lightningnetwork/lnd/brontide" |
|
"github.com/lightningnetwork/lnd/tor" |
|
"github.com/lightningnetwork/lnd/watchtower/lookout" |
|
"github.com/lightningnetwork/lnd/watchtower/wtserver" |
|
) |
|
|
|
// Standalone encapsulates the server-side functionality required by watchtower |
|
// clients. A Standalone couples the two primary subsystems such that, as a |
|
// unit, this instance can negotiate sessions with clients, accept state updates |
|
// for active sessions, monitor the chain for breaches matching known breach |
|
// hints, publish reconstructed justice transactions on behalf of tower clients. |
|
type Standalone struct { |
|
started uint32 // to be used atomically |
|
stopped uint32 // to be used atomically |
|
|
|
cfg *Config |
|
|
|
// listeners is a reference to the wtserver's listeners. |
|
listeners []net.Listener |
|
|
|
// server is the client endpoint, used for negotiating sessions and |
|
// uploading state updates. |
|
server wtserver.Interface |
|
|
|
// lookout is a service that monitors the chain and inspects the |
|
// transactions found in new blocks against the state updates received |
|
// by the server. |
|
lookout lookout.Service |
|
} |
|
|
|
// New validates the passed Config and returns a fresh Standalone instance if |
|
// the tower's subsystems could be properly initialized. |
|
func New(cfg *Config) (*Standalone, error) { |
|
// The tower must have listening address in order to accept new updates |
|
// from clients. |
|
if len(cfg.ListenAddrs) == 0 { |
|
return nil, ErrNoListeners |
|
} |
|
|
|
// Assign the default read timeout if none is provided. |
|
if cfg.ReadTimeout == 0 { |
|
cfg.ReadTimeout = DefaultReadTimeout |
|
} |
|
|
|
// Assign the default write timeout if none is provided. |
|
if cfg.WriteTimeout == 0 { |
|
cfg.WriteTimeout = DefaultWriteTimeout |
|
} |
|
|
|
punisher := lookout.NewBreachPunisher(&lookout.PunisherConfig{ |
|
PublishTx: cfg.PublishTx, |
|
}) |
|
|
|
// Initialize the lookout service with its required resources. |
|
lookout := lookout.New(&lookout.Config{ |
|
BlockFetcher: cfg.BlockFetcher, |
|
DB: cfg.DB, |
|
EpochRegistrar: cfg.EpochRegistrar, |
|
Punisher: punisher, |
|
}) |
|
|
|
// Create a brontide listener on each of the provided listening |
|
// addresses. Client should be able to connect to any of open ports to |
|
// communicate with this Standalone instance. |
|
listeners := make([]net.Listener, 0, len(cfg.ListenAddrs)) |
|
for _, listenAddr := range cfg.ListenAddrs { |
|
listener, err := brontide.NewListener( |
|
cfg.NodeKeyECDH, listenAddr.String(), |
|
) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
listeners = append(listeners, listener) |
|
} |
|
|
|
// Initialize the server with its required resources. |
|
server, err := wtserver.New(&wtserver.Config{ |
|
ChainHash: cfg.ChainHash, |
|
DB: cfg.DB, |
|
NodeKeyECDH: cfg.NodeKeyECDH, |
|
Listeners: listeners, |
|
ReadTimeout: cfg.ReadTimeout, |
|
WriteTimeout: cfg.WriteTimeout, |
|
NewAddress: cfg.NewAddress, |
|
DisableReward: true, |
|
}) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
return &Standalone{ |
|
cfg: cfg, |
|
listeners: listeners, |
|
server: server, |
|
lookout: lookout, |
|
}, nil |
|
} |
|
|
|
// Start idempotently starts the Standalone, an error is returned if the |
|
// subsystems could not be initialized. |
|
func (w *Standalone) Start() error { |
|
if !atomic.CompareAndSwapUint32(&w.started, 0, 1) { |
|
return nil |
|
} |
|
|
|
log.Infof("Starting watchtower") |
|
|
|
// If a tor controller exists in the config, then automatically create a |
|
// hidden service for the watchtower to accept inbound connections from. |
|
if w.cfg.TorController != nil { |
|
log.Infof("Creating watchtower hidden service") |
|
if err := w.createNewHiddenService(); err != nil { |
|
return err |
|
} |
|
} |
|
|
|
if err := w.lookout.Start(); err != nil { |
|
return err |
|
} |
|
if err := w.server.Start(); err != nil { |
|
w.lookout.Stop() |
|
return err |
|
} |
|
|
|
log.Infof("Watchtower started successfully") |
|
|
|
return nil |
|
} |
|
|
|
// Stop idempotently stops the Standalone and blocks until the subsystems have |
|
// completed their shutdown. |
|
func (w *Standalone) Stop() error { |
|
if !atomic.CompareAndSwapUint32(&w.stopped, 0, 1) { |
|
return nil |
|
} |
|
|
|
log.Infof("Stopping watchtower") |
|
|
|
w.server.Stop() |
|
w.lookout.Stop() |
|
|
|
log.Infof("Watchtower stopped successfully") |
|
|
|
return nil |
|
} |
|
|
|
// createNewHiddenService automatically sets up a v2 or v3 onion service in |
|
// order to listen for inbound connections over Tor. |
|
func (w *Standalone) createNewHiddenService() error { |
|
// Get all the ports the watchtower is listening on. These will be used to |
|
// map the hidden service's virtual port. |
|
listenPorts := make([]int, 0, len(w.listeners)) |
|
for _, listener := range w.listeners { |
|
port := listener.Addr().(*net.TCPAddr).Port |
|
listenPorts = append(listenPorts, port) |
|
} |
|
|
|
// Once we've created the port mapping, we can automatically create the |
|
// hidden service. The service's private key will be saved on disk in order |
|
// to persistently have access to this hidden service across restarts. |
|
onionCfg := tor.AddOnionConfig{ |
|
VirtualPort: DefaultPeerPort, |
|
TargetPorts: listenPorts, |
|
Store: tor.NewOnionFile(w.cfg.WatchtowerKeyPath, 0600), |
|
Type: w.cfg.Type, |
|
} |
|
|
|
addr, err := w.cfg.TorController.AddOnion(onionCfg) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
// Append this address to ExternalIPs so that it will be exposed in |
|
// tower info calls. |
|
w.cfg.ExternalIPs = append(w.cfg.ExternalIPs, addr) |
|
|
|
return nil |
|
} |
|
|
|
// PubKey returns the public key for the watchtower used to authentication and |
|
// encrypt traffic with clients. |
|
// |
|
// NOTE: Part of the watchtowerrpc.WatchtowerBackend interface. |
|
func (w *Standalone) PubKey() *btcec.PublicKey { |
|
return w.cfg.NodeKeyECDH.PubKey() |
|
} |
|
|
|
// ListeningAddrs returns the listening addresses where the watchtower server |
|
// can accept client connections. |
|
// |
|
// NOTE: Part of the watchtowerrpc.WatchtowerBackend interface. |
|
func (w *Standalone) ListeningAddrs() []net.Addr { |
|
addrs := make([]net.Addr, 0, len(w.listeners)) |
|
for _, listener := range w.listeners { |
|
addrs = append(addrs, listener.Addr()) |
|
} |
|
|
|
return addrs |
|
} |
|
|
|
// ExternalIPs returns the addresses where the watchtower can be reached by |
|
// clients externally. |
|
// |
|
// NOTE: Part of the watchtowerrpc.WatchtowerBackend interface. |
|
func (w *Standalone) ExternalIPs() []net.Addr { |
|
addrs := make([]net.Addr, 0, len(w.cfg.ExternalIPs)) |
|
addrs = append(addrs, w.cfg.ExternalIPs...) |
|
|
|
return addrs |
|
}
|
|
|