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

lightningnetwork / lnd / 12412879685

19 Dec 2024 12:40PM UTC coverage: 58.744% (+0.09%) from 58.653%
12412879685

Pull #8754

github

ViktorTigerstrom
itest: wrap deriveCustomScopeAccounts at 80 chars

This commit fixes that word wrapping for the deriveCustomScopeAccounts
function docs, and ensures that it wraps at 80 characters or less.
Pull Request #8754: Add `Outbound` Remote Signer implementation

1858 of 2816 new or added lines in 47 files covered. (65.98%)

267 existing lines in 51 files now uncovered.

136038 of 231578 relevant lines covered (58.74%)

19020.65 hits per line

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

35.27
/lnrpc/neutrinorpc/neutrino_server.go
1
//go:build neutrinorpc
2
// +build neutrinorpc
3

4
package neutrinorpc
5

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

12
        "github.com/btcsuite/btcd/blockchain"
13
        "github.com/btcsuite/btcd/chaincfg/chainhash"
14
        "github.com/btcsuite/btcd/wire"
15
        "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
16
        "github.com/lightningnetwork/lnd/lnrpc"
17
        "google.golang.org/grpc"
18
        "gopkg.in/macaroon-bakery.v2/bakery"
19
)
20

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

29
var (
30
        // macPermissions maps RPC calls to the permissions they require.
31
        macPermissions = map[string][]bakery.Op{
32
                "/neutrinorpc.NeutrinoKit/Status": {{
33
                        Entity: "info",
34
                        Action: "read",
35
                }},
36
                "/neutrinorpc.NeutrinoKit/AddPeer": {{
37
                        Entity: "peers",
38
                        Action: "write",
39
                }},
40
                "/neutrinorpc.NeutrinoKit/DisconnectPeer": {{
41
                        Entity: "peers",
42
                        Action: "write",
43
                }},
44
                "/neutrinorpc.NeutrinoKit/IsBanned": {{
45
                        Entity: "info",
46
                        Action: "read",
47
                }},
48
                "/neutrinorpc.NeutrinoKit/GetBlock": {{
49
                        Entity: "onchain",
50
                        Action: "read",
51
                }},
52
                "/neutrinorpc.NeutrinoKit/GetBlockHeader": {{
53
                        Entity: "onchain",
54
                        Action: "read",
55
                }},
56
                "/neutrinorpc.NeutrinoKit/GetCFilter": {{
57
                        Entity: "onchain",
58
                        Action: "read",
59
                }},
60
                "/neutrinorpc.NeutrinoKit/GetBlockHash": {{
61
                        Entity: "onchain",
62
                        Action: "read",
63
                }},
64
        }
65

66
        // ErrNeutrinoNotActive is an error returned when there is no running
67
        // neutrino light client instance.
68
        ErrNeutrinoNotActive = errors.New("no active neutrino instance")
69
)
70

71
// ServerShell is a shell struct holding a reference to the actual sub-server.
72
// It is used to register the gRPC sub-server with the root server before we
73
// have the necessary dependencies to populate the actual sub-server.
74
type ServerShell struct {
75
        NeutrinoKitServer
76
}
77

78
// Server is a sub-server of the main RPC server: the neutrino RPC. This sub
79
// RPC server allows external callers to access the status of the neutrino
80
// currently active within lnd, as well as configuring it at runtime.
81
type Server struct {
82
        injected int32 // To be used atomically.
83

84
        // Required by the grpc-gateway/v2 library for forward compatibility.
85
        // Must be after the atomically used variables to not break struct
86
        // alignment.
87
        UnimplementedNeutrinoKitServer
88

89
        cfg *Config
90
}
91

92
// A compile time check to ensure that NeutrinoKit fully implements the
93
// NeutrinoServer gRPC service.
94
var _ NeutrinoKitServer = (*Server)(nil)
95

96
// New returns a new instance of the neutrinorpc Neutrino sub-server. We also
97
// return the set of permissions for the macaroons that we may create within
98
// this method. If the macaroons we need aren't found in the filepath, then
99
// we'll create them on start up. If we're unable to locate, or create the
100
// macaroons we need, then we'll return with an error.
101
func New() (*Server, lnrpc.MacaroonPerms, error) {
3✔
102
        // We don't create any new macaroons for this subserver, instead reuse
3✔
103
        // existing onchain/offchain permissions.
3✔
104
        return &Server{cfg: &Config{}}, macPermissions, nil
3✔
105
}
3✔
106

107
// Stop signals any active goroutines for a graceful closure.
108
//
109
// NOTE: This is part of the lnrpc.SubServer interface.
110
func (s *Server) Stop() error {
3✔
111
        return nil
3✔
112
}
3✔
113

114
// InjectDependencies populates the sub-server's dependencies. If the
115
// finalizeDependencies boolean is true, then the sub-server will finalize its
116
// dependencies and return an error if any required dependencies are missing.
117
//
118
// NOTE: This is part of the lnrpc.SubServer interface.
119
func (s *Server) InjectDependencies(
120
        configRegistry lnrpc.SubServerConfigDispatcher,
121
        finalizeDependencies bool) error {
3✔
122

3✔
123
        if finalizeDependencies && atomic.AddInt32(&s.injected, 1) != 1 {
3✔
NEW
124
                return lnrpc.ErrDependenciesFinalized
×
NEW
125
        }
×
126

127
        config, err := getConfig(configRegistry, finalizeDependencies)
3✔
128
        if err != nil {
3✔
NEW
129
                return err
×
NEW
130
        }
×
131

132
        s.cfg = config
3✔
133

3✔
134
        return nil
3✔
135
}
136

137
// Name returns a unique string representation of the sub-server. This can be
138
// used to identify the sub-server and also de-duplicate them.
139
//
140
// NOTE: This is part of the lnrpc.SubServer interface.
141
func (s *Server) Name() string {
3✔
142
        return subServerName
3✔
143
}
3✔
144

145
// RegisterWithRootServer will be called by the root gRPC server to direct a
146
// sub RPC server to register itself with the main gRPC root server. Until this
147
// is called, each sub-server won't be able to have
148
// requests routed towards it.
149
//
150
// NOTE: This is part of the lnrpc.GrpcHandler interface.
151
func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
3✔
152
        // We make sure that we register it with the main gRPC server to ensure
3✔
153
        // all our methods are routed properly.
3✔
154
        RegisterNeutrinoKitServer(grpcServer, r)
3✔
155

3✔
156
        log.Debugf("Neutrino RPC server successfully register with root " +
3✔
157
                "gRPC server")
3✔
158

3✔
159
        return nil
3✔
160
}
3✔
161

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

3✔
170
        // We make sure that we register it with the main REST server to ensure
3✔
171
        // all our methods are routed properly.
3✔
172
        err := RegisterNeutrinoKitHandlerFromEndpoint(ctx, mux, dest, opts)
3✔
173
        if err != nil {
3✔
174
                log.Errorf("Could not register Neutrino REST server "+
×
175
                        "with root REST server: %v", err)
×
176
                return err
×
177
        }
×
178

179
        log.Debugf("Neutrino REST server successfully registered with " +
3✔
180
                "root REST server")
3✔
181
        return nil
3✔
182
}
183

184
// CreateSubServer creates an instance of the sub-server, and returns the
185
// macaroon permissions that the sub-server wishes to pass on to the root server
186
// for all methods routed towards it.
187
//
188
// NOTE: This is part of the lnrpc.GrpcHandler interface.
189
func (r *ServerShell) CreateSubServer() (
190
        lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
3✔
191

3✔
192
        subServer, macPermissions, err := New()
3✔
193
        if err != nil {
3✔
194
                return nil, nil, err
×
195
        }
×
196

197
        r.NeutrinoKitServer = subServer
3✔
198
        return subServer, macPermissions, nil
3✔
199
}
200

201
// Status returns the current status, best block height and connected peers
202
// of the neutrino node.
203
//
204
// NOTE: Part of the NeutrinoServer interface.
205
func (s *Server) Status(ctx context.Context,
206
        in *StatusRequest) (*StatusResponse, error) {
1✔
207

1✔
208
        if s.cfg.NeutrinoCS == nil {
1✔
209
                return nil, ErrNeutrinoNotActive
×
210
        }
×
211

212
        bestBlock, err := s.cfg.NeutrinoCS.BestBlock()
1✔
213
        if err != nil {
1✔
214
                return nil, fmt.Errorf("could not get best block: %w", err)
×
215
        }
×
216

217
        peers := s.cfg.NeutrinoCS.Peers()
1✔
218
        var Peers = make([]string, len(peers))
1✔
219
        for i, p := range peers {
2✔
220
                Peers[i] = p.Addr()
1✔
221
        }
1✔
222

223
        return &StatusResponse{
1✔
224
                Active:      s.cfg.NeutrinoCS != nil,
1✔
225
                BlockHeight: bestBlock.Height,
1✔
226
                BlockHash:   bestBlock.Hash.String(),
1✔
227
                Synced:      s.cfg.NeutrinoCS.IsCurrent(),
1✔
228
                Peers:       Peers,
1✔
229
        }, nil
1✔
230
}
231

232
// AddPeer adds a new peer that has already been connected to the server.
233
//
234
// NOTE: Part of the NeutrinoKitServer interface.
235
func (s *Server) AddPeer(ctx context.Context,
236
        in *AddPeerRequest) (*AddPeerResponse, error) {
1✔
237

1✔
238
        if s.cfg.NeutrinoCS == nil {
1✔
239
                return nil, ErrNeutrinoNotActive
×
240
        }
×
241

242
        peer := s.cfg.NeutrinoCS.PeerByAddr(in.PeerAddrs)
1✔
243
        if peer == nil {
1✔
244
                return nil,
×
245
                        fmt.Errorf("could not found peer: %s", in.PeerAddrs)
×
246
        }
×
247
        s.cfg.NeutrinoCS.AddPeer(peer)
1✔
248

1✔
249
        return &AddPeerResponse{}, nil
1✔
250
}
251

252
// DisconnectPeer disconnects a peer by target address. Both outbound and
253
// inbound nodes will be searched for the target node. An error message will
254
// be returned if the peer was not found.
255
//
256
// NOTE: Part of the NeutrinoKitServer interface.
257
func (s *Server) DisconnectPeer(ctx context.Context,
258
        in *DisconnectPeerRequest) (*DisconnectPeerResponse, error) {
×
259

×
260
        if s.cfg.NeutrinoCS == nil {
×
261
                return nil, ErrNeutrinoNotActive
×
262
        }
×
263

264
        peer := s.cfg.NeutrinoCS.PeerByAddr(in.PeerAddrs)
×
265
        if peer == nil {
×
266
                return nil,
×
267
                        fmt.Errorf("could not found peer: %s", in.PeerAddrs)
×
268
        }
×
269

270
        err := s.cfg.NeutrinoCS.DisconnectNodeByAddr(peer.Addr())
×
271
        if err != nil {
×
272
                return nil, err
×
273
        }
×
274

275
        return &DisconnectPeerResponse{}, nil
×
276
}
277

278
// IsBanned returns true if the peer is banned, otherwise false.
279
//
280
// NOTE: Part of the NeutrinoKitServer interface.
281
func (s *Server) IsBanned(ctx context.Context,
282
        in *IsBannedRequest) (*IsBannedResponse, error) {
×
283

×
284
        if s.cfg.NeutrinoCS == nil {
×
285
                return nil, ErrNeutrinoNotActive
×
286
        }
×
287

288
        return &IsBannedResponse{
×
289
                Banned: s.cfg.NeutrinoCS.IsBanned(in.PeerAddrs),
×
290
        }, nil
×
291
}
292

293
// GetBlockHeader returns a block header with a particular block hash. If the
294
// block header is found in the cache, it will be returned immediately.
295
// Otherwise a block will  be requested from the network, one peer at a time,
296
// until one answers.
297
//
298
// NOTE: Part of the NeutrinoKitServer interface.
299
func (s *Server) GetBlockHeader(ctx context.Context,
300
        in *GetBlockHeaderRequest) (*GetBlockHeaderResponse, error) {
×
301

×
302
        if s.cfg.NeutrinoCS == nil {
×
303
                return nil, ErrNeutrinoNotActive
×
304
        }
×
305

306
        var hash chainhash.Hash
×
307
        if err := chainhash.Decode(&hash, in.Hash); err != nil {
×
308
                return nil, err
×
309
        }
×
310

311
        resp, err := s.getBlock(hash)
×
312
        if err != nil {
×
313
                return nil, err
×
314
        }
×
315

316
        return &GetBlockHeaderResponse{
×
317
                Hash:              resp.Hash,
×
318
                Confirmations:     resp.Confirmations,
×
319
                StrippedSize:      resp.StrippedSize,
×
320
                Size:              resp.Size,
×
321
                Weight:            resp.Weight,
×
322
                Height:            resp.Height,
×
323
                Version:           resp.Version,
×
324
                VersionHex:        resp.VersionHex,
×
325
                Merkleroot:        resp.Merkleroot,
×
326
                Time:              resp.Time,
×
327
                Nonce:             resp.Nonce,
×
328
                Bits:              resp.Bits,
×
329
                Ntx:               resp.Ntx,
×
330
                PreviousBlockHash: resp.PreviousBlockHash,
×
331
                RawHex:            resp.RawHex,
×
332
        }, nil
×
333
}
334

335
// GetBlock returns a block with a particular block hash. If the block is
336
// found in the cache, it will be returned immediately. Otherwise a block will
337
// be requested from the network, one peer at a time, until one answers.
338
//
339
// NOTE: Part of the NeutrinoKitServer interface.
340
func (s *Server) GetBlock(ctx context.Context,
341
        in *GetBlockRequest) (*GetBlockResponse, error) {
×
342

×
343
        if s.cfg.NeutrinoCS == nil {
×
344
                return nil, ErrNeutrinoNotActive
×
345
        }
×
346

347
        var hash chainhash.Hash
×
348
        if err := chainhash.Decode(&hash, in.Hash); err != nil {
×
349
                return nil, err
×
350
        }
×
351

352
        return s.getBlock(hash)
×
353
}
354

355
// GetCFilter returns a compact filter of a particular block.
356
// If found, only regular filters will be returned.
357
//
358
// NOTE: Part of the NeutrinoKitServer interface.
359
func (s *Server) GetCFilter(ctx context.Context,
360
        in *GetCFilterRequest) (*GetCFilterResponse, error) {
1✔
361

1✔
362
        if s.cfg.NeutrinoCS == nil {
1✔
363
                return nil, ErrNeutrinoNotActive
×
364
        }
×
365

366
        var hash chainhash.Hash
1✔
367
        if err := chainhash.Decode(&hash, in.Hash); err != nil {
1✔
368
                return nil, err
×
369
        }
×
370

371
        // GetCFilter returns a compact filter from the database. If it is
372
        // missing, it requests the compact filter from the network.
373
        filter, err := s.cfg.NeutrinoCS.GetCFilter(hash, wire.GCSFilterRegular)
1✔
374
        if err != nil {
1✔
375
                return nil, err
×
376
        }
×
377

378
        filterlBytes, err := filter.Bytes()
1✔
379
        if err != nil {
1✔
380
                return nil, err
×
381
        }
×
382

383
        return &GetCFilterResponse{Filter: filterlBytes}, nil
1✔
384
}
385

386
func (s *Server) getBlock(hash chainhash.Hash) (*GetBlockResponse, error) {
×
387
        block, err := s.cfg.NeutrinoCS.GetBlock(hash)
×
388
        if err != nil {
×
389
                return nil, err
×
390
        }
×
391

392
        header, _, err := s.cfg.NeutrinoCS.BlockHeaders.FetchHeader(&hash)
×
393
        if err != nil {
×
394
                return nil, err
×
395
        }
×
396

397
        blockData, err := block.Bytes()
×
398
        if err != nil {
×
399
                return nil, err
×
400
        }
×
401

402
        strippedData, err := block.BytesNoWitness()
×
403
        if err != nil {
×
404
                return nil, err
×
405
        }
×
406

407
        bestBlock, err := s.cfg.NeutrinoCS.BestBlock()
×
408
        if err != nil {
×
409
                return nil, err
×
410
        }
×
411

412
        // Convert txids to a string array.
413
        transactions := block.Transactions()
×
414
        tx := make([]string, len(transactions))
×
415
        for i := range transactions {
×
416
                tx[i] = transactions[i].Hash().String()
×
417
        }
×
418

419
        return &GetBlockResponse{
×
420
                Hash:          block.Hash().String(),
×
421
                Confirmations: int64(1 + bestBlock.Height - block.Height()),
×
422
                StrippedSize:  int64(len(strippedData)),
×
423
                Size:          int64(len(blockData)),
×
424
                Weight:        blockchain.GetBlockWeight(block),
×
425
                Height:        block.Height(),
×
426
                Version:       header.Version,
×
427
                VersionHex:    fmt.Sprintf("%0x", header.Version),
×
428
                Merkleroot:    header.MerkleRoot.String(),
×
429
                Tx:            tx,
×
430
                Time:          header.Timestamp.Unix(),
×
431
                Nonce:         header.Nonce,
×
432
                // Format bits as a hex.
×
433
                Bits:              fmt.Sprintf("%0x", header.Bits),
×
434
                Ntx:               int32(len(block.Transactions())),
×
435
                PreviousBlockHash: header.PrevBlock.String(),
×
436
                RawHex:            blockData,
×
437
        }, nil
×
438
}
439

440
// GetBlockHash returns the header hash of a block at a given height.
441
//
442
// NOTE: Part of the NeutrinoKitServer interface.
443
func (s *Server) GetBlockHash(ctx context.Context,
444
        in *GetBlockHashRequest) (*GetBlockHashResponse, error) {
×
445

×
446
        if s.cfg.NeutrinoCS == nil {
×
447
                return nil, ErrNeutrinoNotActive
×
448
        }
×
449

450
        hash, err := s.cfg.NeutrinoCS.GetBlockHash(int64(in.Height))
×
451
        if err != nil {
×
452
                return nil, err
×
453
        }
×
454

455
        return &GetBlockHashResponse{Hash: hash.String()}, nil
×
456
}
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