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

lightningnetwork / lnd / 14000719599

21 Mar 2025 08:54PM UTC coverage: 58.717% (-10.3%) from 68.989%
14000719599

Pull #8754

github

web-flow
Merge 29f363f18 into 5235f3b24
Pull Request #8754: Add `Outbound` Remote Signer implementation

1562 of 2088 new or added lines in 41 files covered. (74.81%)

28126 existing lines in 464 files now uncovered.

97953 of 166822 relevant lines covered (58.72%)

1.82 hits per line

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

84.03
/lnrpc/peersrpc/peers_server.go
1
//go:build peersrpc
2
// +build peersrpc
3

4
package peersrpc
5

6
import (
7
        "context"
8
        "fmt"
9
        "net"
10
        "sync/atomic"
11

12
        "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
13
        "github.com/lightningnetwork/lnd/feature"
14
        "github.com/lightningnetwork/lnd/lncfg"
15
        "github.com/lightningnetwork/lnd/lnrpc"
16
        "github.com/lightningnetwork/lnd/lnwire"
17
        "github.com/lightningnetwork/lnd/netann"
18
        "google.golang.org/grpc"
19
        "gopkg.in/macaroon-bakery.v2/bakery"
20
)
21

22
const (
23
        // subServerName is the name of the sub rpc server. We'll use this name
24
        // to register ourselves, and we also require that the main
25
        // SubServerConfigDispatcher instance recognize tt as the name of our
26
        // RPC service.
27
        subServerName = "PeersRPC"
28
)
29

30
var (
31
        // macPermissions maps RPC calls to the permissions they require.
32
        macPermissions = map[string][]bakery.Op{
33
                "/peersrpc.Peers/UpdateNodeAnnouncement": {{
34
                        Entity: "peers",
35
                        Action: "write",
36
                }},
37
        }
38
)
39

40
// ServerShell is a shell struct holding a reference to the actual sub-server.
41
// It is used to register the gRPC sub-server with the root server before we
42
// have the necessary dependencies to populate the actual sub-server.
43
type ServerShell struct {
44
        PeersServer
45
}
46

47
// Server is a sub-server of the main RPC server: the peers RPC. This sub
48
// RPC server allows to intereact with our Peers in the Lightning Network.
49
type Server struct {
50
        injected int32 // To be used atomically.
51
        shutdown int32 // To be used atomically.
52

53
        // Required by the grpc-gateway/v2 library for forward compatibility.
54
        // Must be after the atomically used variables to not break struct
55
        // alignment.
56
        UnimplementedPeersServer
57

58
        cfg *Config
59
}
60

61
// A compile time check to ensure that Server fully implements the PeersServer
62
// gRPC service.
63
var _ PeersServer = (*Server)(nil)
64

65
// New returns a new instance of the peersrpc Peers sub-server. We also
66
// return the set of permissions for the macaroons that we may create within
67
// this method. If the macaroons we need aren't found in the filepath, then
68
// we'll create them on start up. If we're unable to locate, or create the
69
// macaroons we need, then we'll return with an error.
70
func New() (*Server, lnrpc.MacaroonPerms, error) {
3✔
71
        return &Server{cfg: &Config{}}, macPermissions, nil
3✔
72
}
3✔
73

74
// Stop signals any active goroutines for a graceful closure.
75
//
76
// NOTE: This is part of the lnrpc.SubServer interface.
77
func (s *Server) Stop() error {
3✔
78
        if atomic.AddInt32(&s.shutdown, 1) != 1 {
3✔
79
                return nil
×
80
        }
×
81

82
        return nil
3✔
83
}
84

85
// InjectDependencies populates the sub-server's dependencies. If the
86
// finalizeDependencies boolean is true, then the sub-server will finalize its
87
// dependencies and return an error if any required dependencies are missing.
88
//
89
// NOTE: This is part of the lnrpc.SubServer interface.
90
func (s *Server) InjectDependencies(
91
        configRegistry lnrpc.SubServerConfigDispatcher,
92
        finalizeDependencies bool) error {
3✔
93

3✔
94
        if finalizeDependencies && atomic.AddInt32(&s.injected, 1) != 1 {
3✔
NEW
95
                return lnrpc.ErrDependenciesFinalized
×
UNCOV
96
        }
×
97

98
        cfg, err := getConfig(configRegistry, finalizeDependencies)
3✔
99
        if err != nil {
3✔
NEW
100
                return err
×
NEW
101
        }
×
102

103
        s.cfg = cfg
3✔
104

3✔
105
        return nil
3✔
106
}
107

108
// Name returns a unique string representation of the sub-server. This can be
109
// used to identify the sub-server and also de-duplicate them.
110
//
111
// NOTE: This is part of the lnrpc.SubServer interface.
112
func (s *Server) Name() string {
3✔
113
        return subServerName
3✔
114
}
3✔
115

116
// RegisterWithRootServer will be called by the root gRPC server to direct a
117
// sub RPC server to register itself with the main gRPC root server. Until this
118
// is called, each sub-server won't be able to have
119
// requests routed towards it.
120
//
121
// NOTE: This is part of the lnrpc.GrpcHandler interface.
122
func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
3✔
123
        // We make sure that we register it with the main gRPC server to ensure
3✔
124
        // all our methods are routed properly.
3✔
125
        RegisterPeersServer(grpcServer, r)
3✔
126

3✔
127
        log.Debugf("Peers RPC server successfully registered with root " +
3✔
128
                "gRPC server")
3✔
129

3✔
130
        return nil
3✔
131
}
3✔
132

133
// RegisterWithRestServer will be called by the root REST mux to direct a sub
134
// RPC server to register itself with the main REST mux server. Until this is
135
// called, each sub-server won't be able to have requests routed towards it.
136
//
137
// NOTE: This is part of the lnrpc.GrpcHandler interface.
138
func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
139
        mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
3✔
140

3✔
141
        // We make sure that we register it with the main REST server to ensure
3✔
142
        // all our methods are routed properly.
3✔
143
        err := RegisterPeersHandlerFromEndpoint(ctx, mux, dest, opts)
3✔
144
        if err != nil {
3✔
145
                log.Errorf("Could not register Peers REST server "+
×
146
                        "with root REST server: %v", err)
×
147
                return err
×
148
        }
×
149

150
        log.Debugf("Peers REST server successfully registered with " +
3✔
151
                "root REST server")
3✔
152
        return nil
3✔
153
}
154

155
// CreateSubServer creates an instance of the sub-server, and returns the
156
// macaroon permissions that the sub-server wishes to pass on to the root server
157
// for all methods routed towards it.
158
//
159
// NOTE: This is part of the lnrpc.GrpcHandler interface.
160
func (r *ServerShell) CreateSubServer() (
161
        lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
3✔
162

3✔
163
        subServer, macPermissions, err := New()
3✔
164
        if err != nil {
3✔
165
                return nil, nil, err
×
166
        }
×
167

168
        r.PeersServer = subServer
3✔
169
        return subServer, macPermissions, nil
3✔
170
}
171

172
// updateAddresses computes the new address set after executing the update
173
// actions.
174
func (s *Server) updateAddresses(currentAddresses []net.Addr,
175
        updates []*UpdateAddressAction) ([]net.Addr, *lnrpc.Op, error) {
3✔
176

3✔
177
        // net.Addr is not comparable so we cannot use the default map
3✔
178
        // (map[net.Addr]struct{}) so we have to use arrays and a helping
3✔
179
        // function.
3✔
180
        findAddr := func(addr net.Addr, slice []net.Addr) bool {
6✔
181
                for _, sAddr := range slice {
6✔
182
                        if sAddr.Network() != addr.Network() {
3✔
183
                                continue
×
184
                        }
185

186
                        if sAddr.String() == addr.String() {
6✔
187
                                return true
3✔
188
                        }
3✔
189
                }
190
                return false
3✔
191
        }
192

193
        // Preallocate enough memory for both arrays.
194
        removeAddr := make([]net.Addr, 0, len(updates))
3✔
195
        addAddr := make([]net.Addr, 0, len(updates))
3✔
196
        for _, update := range updates {
6✔
197
                addr, err := s.cfg.ParseAddr(update.Address)
3✔
198
                if err != nil {
3✔
199
                        return nil, nil, fmt.Errorf("unable to resolve "+
×
200
                                "address %v: %v", update.Address, err)
×
201
                }
×
202

203
                switch update.Action {
3✔
204
                case UpdateAction_ADD:
3✔
205
                        addAddr = append(addAddr, addr)
3✔
206
                case UpdateAction_REMOVE:
3✔
207
                        removeAddr = append(removeAddr, addr)
3✔
208
                default:
×
209
                        return nil, nil, fmt.Errorf("invalid address update "+
×
210
                                "action: %v", update.Action)
×
211
                }
212
        }
213

214
        // Look for any inconsistency trying to add AND remove the same address.
215
        for _, addr := range removeAddr {
6✔
216
                if findAddr(addr, addAddr) {
3✔
217
                        return nil, nil, fmt.Errorf("invalid updates for "+
×
218
                                "removing AND adding %v", addr)
×
219
                }
×
220
        }
221

222
        ops := &lnrpc.Op{Entity: "addresses"}
3✔
223
        newAddrs := make([]net.Addr, 0, len(updates)+len(currentAddresses))
3✔
224

3✔
225
        // Copy current addresses excluding the ones that need to be removed.
3✔
226
        for _, addr := range currentAddresses {
6✔
227
                if findAddr(addr, removeAddr) {
6✔
228
                        ops.Actions = append(
3✔
229
                                ops.Actions,
3✔
230
                                fmt.Sprintf("%s removed", addr.String()),
3✔
231
                        )
3✔
232
                        continue
3✔
233
                }
234
                newAddrs = append(newAddrs, addr)
3✔
235
        }
236

237
        // Add new adresses if needed.
238
        for _, addr := range addAddr {
6✔
239
                if !findAddr(addr, newAddrs) {
6✔
240
                        ops.Actions = append(
3✔
241
                                ops.Actions,
3✔
242
                                fmt.Sprintf("%s added", addr.String()),
3✔
243
                        )
3✔
244
                        newAddrs = append(newAddrs, addr)
3✔
245
                }
3✔
246
        }
247

248
        return newAddrs, ops, nil
3✔
249
}
250

251
// updateFeatures computes the new raw SetNodeAnn after executing the update
252
// actions.
253
func (s *Server) updateFeatures(currentfeatures *lnwire.RawFeatureVector,
254
        updates []*UpdateFeatureAction) (*lnwire.RawFeatureVector,
255
        *lnrpc.Op, error) {
3✔
256

3✔
257
        ops := &lnrpc.Op{Entity: "features"}
3✔
258
        raw := currentfeatures.Clone()
3✔
259

3✔
260
        for _, update := range updates {
6✔
261
                bit := lnwire.FeatureBit(update.FeatureBit)
3✔
262

3✔
263
                switch update.Action {
3✔
264
                case UpdateAction_ADD:
3✔
265
                        if raw.IsSet(bit) {
6✔
266
                                return nil, nil, fmt.Errorf(
3✔
267
                                        "invalid add action for bit %v, "+
3✔
268
                                                "bit is already set",
3✔
269
                                        update.FeatureBit,
3✔
270
                                )
3✔
271
                        }
3✔
272
                        raw.Set(bit)
3✔
273
                        ops.Actions = append(
3✔
274
                                ops.Actions,
3✔
275
                                fmt.Sprintf("%s set", lnwire.Features[bit]),
3✔
276
                        )
3✔
277

278
                case UpdateAction_REMOVE:
3✔
279
                        if !raw.IsSet(bit) {
6✔
280
                                return nil, nil, fmt.Errorf(
3✔
281
                                        "invalid remove action for bit %v, "+
3✔
282
                                                "bit is already unset",
3✔
283
                                        update.FeatureBit,
3✔
284
                                )
3✔
285
                        }
3✔
286
                        raw.Unset(bit)
3✔
287
                        ops.Actions = append(
3✔
288
                                ops.Actions,
3✔
289
                                fmt.Sprintf("%s unset", lnwire.Features[bit]),
3✔
290
                        )
3✔
291

292
                default:
×
293
                        return nil, nil, fmt.Errorf(
×
294
                                "invalid update action (%v) for bit %v",
×
295
                                update.Action,
×
296
                                update.FeatureBit,
×
297
                        )
×
298
                }
299
        }
300

301
        // Validate our new SetNodeAnn.
302
        fv := lnwire.NewFeatureVector(raw, lnwire.Features)
3✔
303
        if err := feature.ValidateDeps(fv); err != nil {
3✔
304
                return nil, nil, fmt.Errorf(
×
305
                        "invalid feature set (SetNodeAnn): %v",
×
306
                        err,
×
307
                )
×
308
        }
×
309

310
        return raw, ops, nil
3✔
311
}
312

313
// UpdateNodeAnnouncement allows the caller to update the node parameters
314
// and broadcasts a new version of the node announcement to its peers.
315
func (s *Server) UpdateNodeAnnouncement(_ context.Context,
316
        req *NodeAnnouncementUpdateRequest) (
317
        *NodeAnnouncementUpdateResponse, error) {
3✔
318

3✔
319
        resp := &NodeAnnouncementUpdateResponse{}
3✔
320
        nodeModifiers := make([]netann.NodeAnnModifier, 0)
3✔
321

3✔
322
        currentNodeAnn := s.cfg.GetNodeAnnouncement()
3✔
323

3✔
324
        nodeAnnFeatures := currentNodeAnn.Features
3✔
325
        featureUpdates := len(req.FeatureUpdates) > 0
3✔
326
        if featureUpdates {
6✔
327
                var (
3✔
328
                        ops *lnrpc.Op
3✔
329
                        err error
3✔
330
                )
3✔
331
                nodeAnnFeatures, ops, err = s.updateFeatures(
3✔
332
                        nodeAnnFeatures, req.FeatureUpdates,
3✔
333
                )
3✔
334
                if err != nil {
6✔
335
                        return nil, fmt.Errorf("error trying to update node "+
3✔
336
                                "features: %w", err)
3✔
337
                }
3✔
338
                resp.Ops = append(resp.Ops, ops)
3✔
339
        }
340

341
        if req.Color != "" {
6✔
342
                color, err := lncfg.ParseHexColor(req.Color)
3✔
343
                if err != nil {
3✔
344
                        return nil, fmt.Errorf("unable to parse color: %w", err)
×
345
                }
×
346

347
                if color != currentNodeAnn.RGBColor {
6✔
348
                        resp.Ops = append(resp.Ops, &lnrpc.Op{
3✔
349
                                Entity: "color",
3✔
350
                                Actions: []string{
3✔
351
                                        fmt.Sprintf("changed to %v", color),
3✔
352
                                },
3✔
353
                        })
3✔
354
                        nodeModifiers = append(
3✔
355
                                nodeModifiers,
3✔
356
                                netann.NodeAnnSetColor(color),
3✔
357
                        )
3✔
358
                }
3✔
359
        }
360

361
        if req.Alias != "" {
6✔
362
                alias, err := lnwire.NewNodeAlias(req.Alias)
3✔
363
                if err != nil {
6✔
364
                        return nil, fmt.Errorf("invalid alias value: %w", err)
3✔
365
                }
3✔
366
                if alias != currentNodeAnn.Alias {
6✔
367
                        resp.Ops = append(resp.Ops, &lnrpc.Op{
3✔
368
                                Entity: "alias",
3✔
369
                                Actions: []string{
3✔
370
                                        fmt.Sprintf("changed to %v", alias),
3✔
371
                                },
3✔
372
                        })
3✔
373
                        nodeModifiers = append(
3✔
374
                                nodeModifiers,
3✔
375
                                netann.NodeAnnSetAlias(alias),
3✔
376
                        )
3✔
377
                }
3✔
378
        }
379

380
        if len(req.AddressUpdates) > 0 {
6✔
381
                newAddrs, ops, err := s.updateAddresses(
3✔
382
                        currentNodeAnn.Addresses,
3✔
383
                        req.AddressUpdates,
3✔
384
                )
3✔
385
                if err != nil {
3✔
386
                        return nil, fmt.Errorf("error trying to update node "+
×
387
                                "addresses: %w", err)
×
388
                }
×
389
                resp.Ops = append(resp.Ops, ops)
3✔
390
                nodeModifiers = append(
3✔
391
                        nodeModifiers,
3✔
392
                        netann.NodeAnnSetAddrs(newAddrs),
3✔
393
                )
3✔
394
        }
395

396
        if len(nodeModifiers) == 0 && !featureUpdates {
6✔
397
                return nil, fmt.Errorf("unable to detect any new values to " +
3✔
398
                        "update the node announcement")
3✔
399
        }
3✔
400

401
        if err := s.cfg.UpdateNodeAnnouncement(
3✔
402
                nodeAnnFeatures, nodeModifiers...,
3✔
403
        ); err != nil {
6✔
404
                return nil, err
3✔
405
        }
3✔
406

407
        return resp, nil
3✔
408
}
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