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 }