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

lightningnetwork / lnd / 13974489001

20 Mar 2025 04:32PM UTC coverage: 56.292% (-2.9%) from 59.168%
13974489001

Pull #8754

github

web-flow
Merge aed149e6b into ea050d06f
Pull Request #8754: Add `Outbound` Remote Signer implementation

594 of 1713 new or added lines in 26 files covered. (34.68%)

23052 existing lines in 272 files now uncovered.

105921 of 188165 relevant lines covered (56.29%)

23796.34 hits per line

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

0.0
/lnrpc/devrpc/dev_server.go
1
//go:build dev
2
// +build dev
3

4
package devrpc
5

6
import (
7
        "context"
8
        "encoding/hex"
9
        "fmt"
10
        "strconv"
11
        "strings"
12
        "sync/atomic"
13
        "time"
14

15
        "github.com/btcsuite/btcd/btcutil"
16
        "github.com/btcsuite/btcd/chaincfg/chainhash"
17
        "github.com/btcsuite/btcd/wire"
18
        "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
19
        "github.com/lightningnetwork/lnd/fn/v2"
20
        "github.com/lightningnetwork/lnd/graph/db/models"
21
        "github.com/lightningnetwork/lnd/lncfg"
22
        "github.com/lightningnetwork/lnd/lnrpc"
23
        "github.com/lightningnetwork/lnd/lntypes"
24
        "github.com/lightningnetwork/lnd/lnwire"
25
        "google.golang.org/grpc"
26
        "gopkg.in/macaroon-bakery.v2/bakery"
27
)
28

29
const (
30
        // subServerName is the name of the sub rpc server. We'll use this name
31
        // to register ourselves, and we also require that the main
32
        // SubServerConfigDispatcher instance recognize tt as the name of our
33
        // RPC service.
34
        subServerName = "DevRPC"
35
)
36

37
var (
38
        // macPermissions maps RPC calls to the permissions they require.
39
        macPermissions = map[string][]bakery.Op{
40
                "/devrpc.Dev/ImportGraph": {{
41
                        Entity: "offchain",
42
                        Action: "write",
43
                }},
44
                "/devrpc.Dev/Quiesce": {{
45
                        Entity: "offchain",
46
                        Action: "write",
47
                }},
48
        }
49
)
50

51
// ServerShell is a shell struct holding a reference to the actual sub-server.
52
// It is used to register the gRPC sub-server with the root server before we
53
// have the necessary dependencies to populate the actual sub-server.
54
type ServerShell struct {
55
        DevServer
56
}
57

58
// Server is a sub-server of the main RPC server: the dev RPC. This sub
59
// RPC server allows developers to set and query LND state that is not possible
60
// during normal operation.
61
type Server struct {
62
        injected int32 // To be used atomically.
63
        shutdown int32 // To be used atomically.
64
        quit     chan struct{}
65

66
        // Required by the grpc-gateway/v2 library for forward compatibility.
67
        // Must be after the atomically used variables to not break struct
68
        // alignment.
69
        UnimplementedDevServer
70

71
        cfg *Config
72
}
73

74
// A compile time check to ensure that Server fully implements the
75
// DevServer gRPC service.
76
var _ DevServer = (*Server)(nil)
77

78
// New returns a new instance of the devrpc Dev sub-server. We also return the
79
// set of permissions for the macaroons that we may create within this method.
80
// If the macaroons we need aren't found in the filepath, then we'll create them
81
// on start up. If we're unable to locate, or create the macaroons we need, then
82
// we'll return with an error.
NEW
83
func New() (*Server, lnrpc.MacaroonPerms, error) {
×
UNCOV
84
        // We don't create any new macaroons for this subserver, instead reuse
×
UNCOV
85
        // existing onchain/offchain permissions.
×
UNCOV
86
        server := &Server{
×
UNCOV
87
                quit: make(chan struct{}),
×
NEW
88
                cfg:  &Config{},
×
UNCOV
89
        }
×
UNCOV
90

×
UNCOV
91
        return server, macPermissions, nil
×
UNCOV
92
}
×
93

94
// Stop signals any active goroutines for a graceful closure.
95
//
96
// NOTE: This is part of the lnrpc.SubServer interface.
NEW
97
func (s *Server) Stop() error {
×
NEW
98
        if atomic.AddInt32(&s.shutdown, 1) != 1 {
×
99
                return nil
×
100
        }
×
101

NEW
102
        close(s.quit)
×
NEW
103

×
UNCOV
104
        return nil
×
105
}
106

107
// InjectDependencies populates the sub-server's dependencies. If the
108
// finalizeDependencies boolean is true, then the sub-server will finalize its
109
// dependencies and return an error if any required dependencies are missing.
110
//
111
// NOTE: This is part of the lnrpc.SubServer interface.
112
func (s *Server) InjectDependencies(
113
        configRegistry lnrpc.SubServerConfigDispatcher,
NEW
114
        finalizeDependencies bool) error {
×
NEW
115

×
NEW
116
        if finalizeDependencies && atomic.AddInt32(&s.injected, 1) != 1 {
×
NEW
117
                return lnrpc.ErrDependenciesFinalized
×
UNCOV
118
        }
×
119

NEW
120
        cfg, err := getConfig(configRegistry, finalizeDependencies)
×
NEW
121
        if err != nil {
×
NEW
122
                return err
×
NEW
123
        }
×
124

NEW
125
        s.cfg = cfg
×
UNCOV
126

×
UNCOV
127
        return nil
×
128
}
129

130
// Name returns a unique string representation of the sub-server. This can be
131
// used to identify the sub-server and also de-duplicate them.
132
//
133
// NOTE: This is part of the lnrpc.SubServer interface.
UNCOV
134
func (s *Server) Name() string {
×
UNCOV
135
        return subServerName
×
UNCOV
136
}
×
137

138
// RegisterWithRootServer will be called by the root gRPC server to direct a
139
// sub RPC server to register itself with the main gRPC root server. Until this
140
// is called, each sub-server won't be able to have
141
// requests routed towards it.
142
//
143
// NOTE: This is part of the lnrpc.GrpcHandler interface.
UNCOV
144
func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
×
UNCOV
145
        // We make sure that we register it with the main gRPC server to ensure
×
UNCOV
146
        // all our methods are routed properly.
×
UNCOV
147
        RegisterDevServer(grpcServer, r)
×
UNCOV
148

×
UNCOV
149
        log.Debugf("DEV RPC server successfully registered with root the " +
×
UNCOV
150
                "gRPC server")
×
UNCOV
151

×
UNCOV
152
        return nil
×
UNCOV
153
}
×
154

155
// RegisterWithRestServer will be called by the root REST mux to direct a sub
156
// RPC server to register itself with the main REST mux server. Until this is
157
// called, each sub-server won't be able to have requests routed towards it.
158
//
159
// NOTE: This is part of the lnrpc.GrpcHandler interface.
160
func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
UNCOV
161
        mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
×
UNCOV
162

×
UNCOV
163
        // We make sure that we register it with the main REST server to ensure
×
UNCOV
164
        // all our methods are routed properly.
×
UNCOV
165
        err := RegisterDevHandlerFromEndpoint(ctx, mux, dest, opts)
×
UNCOV
166
        if err != nil {
×
167
                log.Errorf("Could not register DEV REST server with the root "+
×
168
                        "REST server: %v", err)
×
169
                return err
×
170
        }
×
171

UNCOV
172
        log.Debugf("DEV REST server successfully registered with the root " +
×
UNCOV
173
                "REST server")
×
UNCOV
174
        return nil
×
175
}
176

177
// CreateSubServer creates an instance of the sub-server, and returns the
178
// macaroon permissions that the sub-server wishes to pass on to the root server
179
// for all methods routed towards it.
180
//
181
// NOTE: This is part of the lnrpc.GrpcHandler interface.
182
func (r *ServerShell) CreateSubServer() (
UNCOV
183
        lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
×
UNCOV
184

×
NEW
185
        subServer, macPermissions, err := New()
×
UNCOV
186
        if err != nil {
×
187
                return nil, nil, err
×
188
        }
×
189

UNCOV
190
        r.DevServer = subServer
×
UNCOV
191
        return subServer, macPermissions, nil
×
192
}
193

194
func parseOutPoint(s string) (*wire.OutPoint, error) {
×
195
        split := strings.Split(s, ":")
×
196
        if len(split) != 2 {
×
197
                return nil, fmt.Errorf("expecting outpoint to be in format of: " +
×
198
                        "txid:index")
×
199
        }
×
200

201
        index, err := strconv.ParseInt(split[1], 10, 32)
×
202
        if err != nil {
×
203
                return nil, fmt.Errorf("unable to decode output index: %w", err)
×
204
        }
×
205

206
        txid, err := chainhash.NewHashFromStr(split[0])
×
207
        if err != nil {
×
208
                return nil, fmt.Errorf("unable to parse hex string: %w", err)
×
209
        }
×
210

211
        return &wire.OutPoint{
×
212
                Hash:  *txid,
×
213
                Index: uint32(index),
×
214
        }, nil
×
215
}
216

217
func parsePubKey(pubKeyStr string) ([33]byte, error) {
×
218
        var pubKey [33]byte
×
219
        pubKeyBytes, err := hex.DecodeString(pubKeyStr)
×
220
        if err != nil || len(pubKeyBytes) != 33 {
×
221
                return pubKey, fmt.Errorf("invalid pubkey: %v", pubKeyStr)
×
222
        }
×
223

224
        copy(pubKey[:], pubKeyBytes)
×
225
        return pubKey, nil
×
226
}
227

228
// ImportGraph imports a graph dump (without auth proofs).
229
//
230
// NOTE: Part of the DevServer interface.
231
func (s *Server) ImportGraph(ctx context.Context,
232
        graph *lnrpc.ChannelGraph) (*ImportGraphResponse, error) {
×
233

×
234
        // Obtain the pointer to the global singleton channel graph.
×
235
        graphDB := s.cfg.GraphDB
×
236

×
237
        var err error
×
238
        for _, rpcNode := range graph.Nodes {
×
239
                node := &models.LightningNode{
×
240
                        HaveNodeAnnouncement: true,
×
241
                        LastUpdate: time.Unix(
×
242
                                int64(rpcNode.LastUpdate), 0,
×
243
                        ),
×
244
                        Alias: rpcNode.Alias,
×
245
                }
×
246

×
247
                node.PubKeyBytes, err = parsePubKey(rpcNode.PubKey)
×
248
                if err != nil {
×
249
                        return nil, err
×
250
                }
×
251

252
                featureBits := make([]lnwire.FeatureBit, 0, len(rpcNode.Features))
×
253
                featureNames := make(map[lnwire.FeatureBit]string)
×
254

×
255
                for featureBit, feature := range rpcNode.Features {
×
256
                        featureBits = append(
×
257
                                featureBits, lnwire.FeatureBit(featureBit),
×
258
                        )
×
259

×
260
                        featureNames[lnwire.FeatureBit(featureBit)] = feature.Name
×
261
                }
×
262

263
                featureVector := lnwire.NewRawFeatureVector(featureBits...)
×
264
                node.Features = lnwire.NewFeatureVector(
×
265
                        featureVector, featureNames,
×
266
                )
×
267

×
268
                node.Color, err = lncfg.ParseHexColor(rpcNode.Color)
×
269
                if err != nil {
×
270
                        return nil, err
×
271
                }
×
272

273
                if err := graphDB.AddLightningNode(node); err != nil {
×
274
                        return nil, fmt.Errorf("unable to add node %v: %w",
×
275
                                rpcNode.PubKey, err)
×
276
                }
×
277

278
                log.Debugf("Imported node: %v", rpcNode.PubKey)
×
279
        }
280

281
        for _, rpcEdge := range graph.Edges {
×
282
                rpcEdge := rpcEdge
×
283

×
284
                edge := &models.ChannelEdgeInfo{
×
285
                        ChannelID: rpcEdge.ChannelId,
×
286
                        ChainHash: *s.cfg.ActiveNetParams.GenesisHash,
×
287
                        Capacity:  btcutil.Amount(rpcEdge.Capacity),
×
288
                }
×
289

×
290
                edge.NodeKey1Bytes, err = parsePubKey(rpcEdge.Node1Pub)
×
291
                if err != nil {
×
292
                        return nil, err
×
293
                }
×
294

295
                edge.NodeKey2Bytes, err = parsePubKey(rpcEdge.Node2Pub)
×
296
                if err != nil {
×
297
                        return nil, err
×
298
                }
×
299

300
                channelPoint, err := parseOutPoint(rpcEdge.ChanPoint)
×
301
                if err != nil {
×
302
                        return nil, err
×
303
                }
×
304
                edge.ChannelPoint = *channelPoint
×
305

×
306
                if err := graphDB.AddChannelEdge(edge); err != nil {
×
307
                        return nil, fmt.Errorf("unable to add edge %v: %w",
×
308
                                rpcEdge.ChanPoint, err)
×
309
                }
×
310

311
                makePolicy := func(rpcPolicy *lnrpc.RoutingPolicy) *models.ChannelEdgePolicy { //nolint:ll
×
312
                        policy := &models.ChannelEdgePolicy{
×
313
                                ChannelID: rpcEdge.ChannelId,
×
314
                                LastUpdate: time.Unix(
×
315
                                        int64(rpcPolicy.LastUpdate), 0,
×
316
                                ),
×
317
                                TimeLockDelta: uint16(
×
318
                                        rpcPolicy.TimeLockDelta,
×
319
                                ),
×
320
                                MinHTLC: lnwire.MilliSatoshi(
×
321
                                        rpcPolicy.MinHtlc,
×
322
                                ),
×
323
                                FeeBaseMSat: lnwire.MilliSatoshi(
×
324
                                        rpcPolicy.FeeBaseMsat,
×
325
                                ),
×
326
                                FeeProportionalMillionths: lnwire.MilliSatoshi(
×
327
                                        rpcPolicy.FeeRateMilliMsat,
×
328
                                ),
×
329
                        }
×
330
                        if rpcPolicy.MaxHtlcMsat > 0 {
×
331
                                policy.MaxHTLC = lnwire.MilliSatoshi(
×
332
                                        rpcPolicy.MaxHtlcMsat,
×
333
                                )
×
334
                                policy.MessageFlags |=
×
335
                                        lnwire.ChanUpdateRequiredMaxHtlc
×
336
                        }
×
337

338
                        return policy
×
339
                }
340

341
                if rpcEdge.Node1Policy != nil {
×
342
                        policy := makePolicy(rpcEdge.Node1Policy)
×
343
                        policy.ChannelFlags = 0
×
344
                        if err := graphDB.UpdateEdgePolicy(policy); err != nil {
×
345
                                return nil, fmt.Errorf(
×
346
                                        "unable to update policy: %v", err)
×
347
                        }
×
348
                }
349

350
                if rpcEdge.Node2Policy != nil {
×
351
                        policy := makePolicy(rpcEdge.Node2Policy)
×
352
                        policy.ChannelFlags = 1
×
353
                        if err := graphDB.UpdateEdgePolicy(policy); err != nil {
×
354
                                return nil, fmt.Errorf(
×
355
                                        "unable to update policy: %v", err)
×
356
                        }
×
357
                }
358

359
                log.Debugf("Added edge: %v", rpcEdge.ChannelId)
×
360
        }
361

362
        return &ImportGraphResponse{}, nil
×
363
}
364

365
// Quiesce initiates the quiescence process for the channel with the given
366
// channel ID. This method will block until the channel is fully quiesced.
367
func (s *Server) Quiesce(_ context.Context, in *QuiescenceRequest) (
UNCOV
368
        *QuiescenceResponse, error) {
×
UNCOV
369

×
UNCOV
370
        txid, err := lnrpc.GetChanPointFundingTxid(in.ChanId)
×
UNCOV
371
        if err != nil {
×
372
                return nil, err
×
373
        }
×
374

UNCOV
375
        op := wire.NewOutPoint(txid, in.ChanId.OutputIndex)
×
UNCOV
376
        cid := lnwire.NewChanIDFromOutPoint(*op)
×
UNCOV
377
        ln, err := s.cfg.Switch.GetLink(cid)
×
UNCOV
378
        if err != nil {
×
379
                return nil, err
×
380
        }
×
381

UNCOV
382
        select {
×
UNCOV
383
        case result := <-ln.InitStfu():
×
UNCOV
384
                mkResp := func(b lntypes.ChannelParty) *QuiescenceResponse {
×
UNCOV
385
                        return &QuiescenceResponse{Initiator: b.IsLocal()}
×
UNCOV
386
                }
×
387

UNCOV
388
                return fn.MapOk(mkResp)(result).Unpack()
×
389

390
        case <-s.quit:
×
391
                return nil, fmt.Errorf("server shutting down")
×
392
        }
393
}
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