github.com/decred/dcrlnd@v0.7.6/watchtower/standalone.go (about)

     1  package watchtower
     2  
     3  import (
     4  	"net"
     5  	"sync/atomic"
     6  
     7  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
     8  	"github.com/decred/dcrlnd/brontide"
     9  	"github.com/decred/dcrlnd/tor"
    10  	"github.com/decred/dcrlnd/watchtower/lookout"
    11  	"github.com/decred/dcrlnd/watchtower/wtserver"
    12  )
    13  
    14  // Standalone encapsulates the server-side functionality required by watchtower
    15  // clients. A Standalone couples the two primary subsystems such that, as a
    16  // unit, this instance can negotiate sessions with clients, accept state updates
    17  // for active sessions, monitor the chain for breaches matching known breach
    18  // hints, publish reconstructed justice transactions on behalf of tower clients.
    19  type Standalone struct {
    20  	started uint32 // to be used atomically
    21  	stopped uint32 // to be used atomically
    22  
    23  	cfg *Config
    24  
    25  	// listeners is a reference to the wtserver's listeners.
    26  	listeners []net.Listener
    27  
    28  	// server is the client endpoint, used for negotiating sessions and
    29  	// uploading state updates.
    30  	server wtserver.Interface
    31  
    32  	// lookout is a service that monitors the chain and inspects the
    33  	// transactions found in new blocks against the state updates received
    34  	// by the server.
    35  	lookout lookout.Service
    36  }
    37  
    38  // New validates the passed Config and returns a fresh Standalone instance if
    39  // the tower's subsystems could be properly initialized.
    40  func New(cfg *Config) (*Standalone, error) {
    41  	// The tower must have listening address in order to accept new updates
    42  	// from clients.
    43  	if len(cfg.ListenAddrs) == 0 {
    44  		return nil, ErrNoListeners
    45  	}
    46  
    47  	// Assign the default read timeout if none is provided.
    48  	if cfg.ReadTimeout == 0 {
    49  		cfg.ReadTimeout = DefaultReadTimeout
    50  	}
    51  
    52  	// Assign the default write timeout if none is provided.
    53  	if cfg.WriteTimeout == 0 {
    54  		cfg.WriteTimeout = DefaultWriteTimeout
    55  	}
    56  
    57  	punisher := lookout.NewBreachPunisher(&lookout.PunisherConfig{
    58  		PublishTx: cfg.PublishTx,
    59  	})
    60  
    61  	// Initialize the lookout service with its required resources.
    62  	lookout := lookout.New(&lookout.Config{
    63  		NetParams:      cfg.NetParams,
    64  		BlockFetcher:   cfg.BlockFetcher,
    65  		DB:             cfg.DB,
    66  		EpochRegistrar: cfg.EpochRegistrar,
    67  		Punisher:       punisher,
    68  	})
    69  
    70  	// Create a brontide listener on each of the provided listening
    71  	// addresses. Client should be able to connect to any of open ports to
    72  	// communicate with this Standalone instance.
    73  	listeners := make([]net.Listener, 0, len(cfg.ListenAddrs))
    74  	for _, listenAddr := range cfg.ListenAddrs {
    75  		listener, err := brontide.NewListener(
    76  			cfg.NodeKeyECDH, listenAddr.String(),
    77  		)
    78  		if err != nil {
    79  			return nil, err
    80  		}
    81  
    82  		listeners = append(listeners, listener)
    83  	}
    84  
    85  	// Initialize the server with its required resources.
    86  	server, err := wtserver.New(&wtserver.Config{
    87  		ChainHash:     cfg.ChainHash,
    88  		DB:            cfg.DB,
    89  		NodeKeyECDH:   cfg.NodeKeyECDH,
    90  		Listeners:     listeners,
    91  		ReadTimeout:   cfg.ReadTimeout,
    92  		WriteTimeout:  cfg.WriteTimeout,
    93  		NewAddress:    cfg.NewAddress,
    94  		DisableReward: true,
    95  	})
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	return &Standalone{
   101  		cfg:       cfg,
   102  		listeners: listeners,
   103  		server:    server,
   104  		lookout:   lookout,
   105  	}, nil
   106  }
   107  
   108  // Start idempotently starts the Standalone, an error is returned if the
   109  // subsystems could not be initialized.
   110  func (w *Standalone) Start() error {
   111  	if !atomic.CompareAndSwapUint32(&w.started, 0, 1) {
   112  		return nil
   113  	}
   114  
   115  	log.Infof("Starting watchtower")
   116  
   117  	// If a tor controller exists in the config, then automatically create a
   118  	// hidden service for the watchtower to accept inbound connections from.
   119  	if w.cfg.TorController != nil {
   120  		log.Infof("Creating watchtower hidden service")
   121  		if err := w.createNewHiddenService(); err != nil {
   122  			return err
   123  		}
   124  	}
   125  
   126  	if err := w.lookout.Start(); err != nil {
   127  		return err
   128  	}
   129  	if err := w.server.Start(); err != nil {
   130  		w.lookout.Stop()
   131  		return err
   132  	}
   133  
   134  	log.Infof("Watchtower started successfully")
   135  
   136  	return nil
   137  }
   138  
   139  // Stop idempotently stops the Standalone and blocks until the subsystems have
   140  // completed their shutdown.
   141  func (w *Standalone) Stop() error {
   142  	if !atomic.CompareAndSwapUint32(&w.stopped, 0, 1) {
   143  		return nil
   144  	}
   145  
   146  	log.Infof("Stopping watchtower")
   147  
   148  	w.server.Stop()
   149  	w.lookout.Stop()
   150  
   151  	log.Infof("Watchtower stopped successfully")
   152  
   153  	return nil
   154  }
   155  
   156  // createNewHiddenService automatically sets up a v2 or v3 onion service in
   157  // order to listen for inbound connections over Tor.
   158  func (w *Standalone) createNewHiddenService() error {
   159  	// Get all the ports the watchtower is listening on. These will be used to
   160  	// map the hidden service's virtual port.
   161  	listenPorts := make([]int, 0, len(w.listeners))
   162  	for _, listener := range w.listeners {
   163  		port := listener.Addr().(*net.TCPAddr).Port
   164  		listenPorts = append(listenPorts, port)
   165  	}
   166  
   167  	// Once we've created the port mapping, we can automatically create the
   168  	// hidden service. The service's private key will be saved on disk in order
   169  	// to persistently have access to this hidden service across restarts.
   170  	onionCfg := tor.AddOnionConfig{
   171  		VirtualPort: DefaultPeerPort,
   172  		TargetPorts: listenPorts,
   173  		Store:       tor.NewOnionFile(w.cfg.WatchtowerKeyPath, 0600),
   174  		Type:        w.cfg.Type,
   175  	}
   176  
   177  	addr, err := w.cfg.TorController.AddOnion(onionCfg)
   178  	if err != nil {
   179  		return err
   180  	}
   181  
   182  	// Append this address to ExternalIPs so that it will be exposed in
   183  	// tower info calls.
   184  	w.cfg.ExternalIPs = append(w.cfg.ExternalIPs, addr)
   185  
   186  	return nil
   187  }
   188  
   189  // PubKey returns the public key for the watchtower used to authentication and
   190  // encrypt traffic with clients.
   191  //
   192  // NOTE: Part of the watchtowerrpc.WatchtowerBackend interface.
   193  func (w *Standalone) PubKey() *secp256k1.PublicKey {
   194  	return w.cfg.NodeKeyECDH.PubKey()
   195  }
   196  
   197  // ListeningAddrs returns the listening addresses where the watchtower server
   198  // can accept client connections.
   199  //
   200  // NOTE: Part of the watchtowerrpc.WatchtowerBackend interface.
   201  func (w *Standalone) ListeningAddrs() []net.Addr {
   202  	addrs := make([]net.Addr, 0, len(w.listeners))
   203  	for _, listener := range w.listeners {
   204  		addrs = append(addrs, listener.Addr())
   205  	}
   206  
   207  	return addrs
   208  }
   209  
   210  // ExternalIPs returns the addresses where the watchtower can be reached by
   211  // clients externally.
   212  //
   213  // NOTE: Part of the watchtowerrpc.WatchtowerBackend interface.
   214  func (w *Standalone) ExternalIPs() []net.Addr {
   215  	addrs := make([]net.Addr, 0, len(w.cfg.ExternalIPs))
   216  	addrs = append(addrs, w.cfg.ExternalIPs...)
   217  
   218  	return addrs
   219  }