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

lightningnetwork / lnd / 12428593038

20 Dec 2024 09:02AM UTC coverage: 58.33% (-0.2%) from 58.576%
12428593038

Pull #9382

github

guggero
.golangci.yml: speed up linter by updating start commit

With this we allow the linter to only look at recent changes, since
everything between that old commit and this most recent one has been
linted correctly anyway.
Pull Request #9382: lint: deprecate old linters, use new ref commit

133769 of 229330 relevant lines covered (58.33%)

19284.53 hits per line

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

19.63
/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

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

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

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

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

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

77
// Server is a sub-server of the main RPC server: the neutrino RPC. This sub
78
// RPC server allows external callers to access the status of the neutrino
79
// currently active within lnd, as well as configuring it at runtime.
80
type Server struct {
81
        // Required by the grpc-gateway/v2 library for forward compatibility.
82
        // Must be after the atomically used variables to not break struct
83
        // alignment.
84
        UnimplementedNeutrinoKitServer
85

86
        cfg *Config
87
}
88

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

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

1✔
105
        return server, macPermissions, nil
1✔
106
}
1✔
107

108
// Start launches any helper goroutines required for the Server to function.
109
//
110
// NOTE: This is part of the lnrpc.SubServer interface.
111
func (s *Server) Start() error {
1✔
112
        return nil
1✔
113
}
1✔
114

115
// Stop signals any active goroutines for a graceful closure.
116
//
117
// NOTE: This is part of the lnrpc.SubServer interface.
118
func (s *Server) Stop() error {
1✔
119
        return nil
1✔
120
}
1✔
121

122
// Name returns a unique string representation of the sub-server. This can be
123
// used to identify the sub-server and also de-duplicate them.
124
//
125
// NOTE: This is part of the lnrpc.SubServer interface.
126
func (s *Server) Name() string {
1✔
127
        return subServerName
1✔
128
}
1✔
129

130
// RegisterWithRootServer will be called by the root gRPC server to direct a
131
// sub RPC server to register itself with the main gRPC root server. Until this
132
// is called, each sub-server won't be able to have
133
// requests routed towards it.
134
//
135
// NOTE: This is part of the lnrpc.GrpcHandler interface.
136
func (r *ServerShell) RegisterWithRootServer(grpcServer *grpc.Server) error {
1✔
137
        // We make sure that we register it with the main gRPC server to ensure
1✔
138
        // all our methods are routed properly.
1✔
139
        RegisterNeutrinoKitServer(grpcServer, r)
1✔
140

1✔
141
        log.Debugf("Neutrino RPC server successfully register with root " +
1✔
142
                "gRPC server")
1✔
143

1✔
144
        return nil
1✔
145
}
1✔
146

147
// RegisterWithRestServer will be called by the root REST mux to direct a sub
148
// RPC server to register itself with the main REST mux server. Until this is
149
// called, each sub-server won't be able to have requests routed towards it.
150
//
151
// NOTE: This is part of the lnrpc.GrpcHandler interface.
152
func (r *ServerShell) RegisterWithRestServer(ctx context.Context,
153
        mux *runtime.ServeMux, dest string, opts []grpc.DialOption) error {
1✔
154

1✔
155
        // We make sure that we register it with the main REST server to ensure
1✔
156
        // all our methods are routed properly.
1✔
157
        err := RegisterNeutrinoKitHandlerFromEndpoint(ctx, mux, dest, opts)
1✔
158
        if err != nil {
1✔
159
                log.Errorf("Could not register Neutrino REST server "+
×
160
                        "with root REST server: %v", err)
×
161
                return err
×
162
        }
×
163

164
        log.Debugf("Neutrino REST server successfully registered with " +
1✔
165
                "root REST server")
1✔
166
        return nil
1✔
167
}
168

169
// CreateSubServer populates the subserver's dependencies using the passed
170
// SubServerConfigDispatcher. This method should fully initialize the
171
// sub-server instance, making it ready for action. It returns the macaroon
172
// permissions that the sub-server wishes to pass on to the root server for all
173
// methods routed towards it.
174
//
175
// NOTE: This is part of the lnrpc.GrpcHandler interface.
176
func (r *ServerShell) CreateSubServer(configRegistry lnrpc.SubServerConfigDispatcher) (
177
        lnrpc.SubServer, lnrpc.MacaroonPerms, error) {
1✔
178

1✔
179
        subServer, macPermissions, err := createNewSubServer(configRegistry)
1✔
180
        if err != nil {
1✔
181
                return nil, nil, err
×
182
        }
×
183

184
        r.NeutrinoKitServer = subServer
1✔
185
        return subServer, macPermissions, nil
1✔
186
}
187

188
// Status returns the current status, best block height and connected peers
189
// of the neutrino node.
190
//
191
// NOTE: Part of the NeutrinoServer interface.
192
func (s *Server) Status(ctx context.Context,
193
        in *StatusRequest) (*StatusResponse, error) {
×
194

×
195
        if s.cfg.NeutrinoCS == nil {
×
196
                return nil, ErrNeutrinoNotActive
×
197
        }
×
198

199
        bestBlock, err := s.cfg.NeutrinoCS.BestBlock()
×
200
        if err != nil {
×
201
                return nil, fmt.Errorf("could not get best block: %w", err)
×
202
        }
×
203

204
        peers := s.cfg.NeutrinoCS.Peers()
×
205
        var Peers = make([]string, len(peers))
×
206
        for i, p := range peers {
×
207
                Peers[i] = p.Addr()
×
208
        }
×
209

210
        return &StatusResponse{
×
211
                Active:      s.cfg.NeutrinoCS != nil,
×
212
                BlockHeight: bestBlock.Height,
×
213
                BlockHash:   bestBlock.Hash.String(),
×
214
                Synced:      s.cfg.NeutrinoCS.IsCurrent(),
×
215
                Peers:       Peers,
×
216
        }, nil
×
217
}
218

219
// AddPeer adds a new peer that has already been connected to the server.
220
//
221
// NOTE: Part of the NeutrinoKitServer interface.
222
func (s *Server) AddPeer(ctx context.Context,
223
        in *AddPeerRequest) (*AddPeerResponse, error) {
×
224

×
225
        if s.cfg.NeutrinoCS == nil {
×
226
                return nil, ErrNeutrinoNotActive
×
227
        }
×
228

229
        peer := s.cfg.NeutrinoCS.PeerByAddr(in.PeerAddrs)
×
230
        if peer == nil {
×
231
                return nil,
×
232
                        fmt.Errorf("could not found peer: %s", in.PeerAddrs)
×
233
        }
×
234
        s.cfg.NeutrinoCS.AddPeer(peer)
×
235

×
236
        return &AddPeerResponse{}, nil
×
237
}
238

239
// DisconnectPeer disconnects a peer by target address. Both outbound and
240
// inbound nodes will be searched for the target node. An error message will
241
// be returned if the peer was not found.
242
//
243
// NOTE: Part of the NeutrinoKitServer interface.
244
func (s *Server) DisconnectPeer(ctx context.Context,
245
        in *DisconnectPeerRequest) (*DisconnectPeerResponse, error) {
×
246

×
247
        if s.cfg.NeutrinoCS == nil {
×
248
                return nil, ErrNeutrinoNotActive
×
249
        }
×
250

251
        peer := s.cfg.NeutrinoCS.PeerByAddr(in.PeerAddrs)
×
252
        if peer == nil {
×
253
                return nil,
×
254
                        fmt.Errorf("could not found peer: %s", in.PeerAddrs)
×
255
        }
×
256

257
        err := s.cfg.NeutrinoCS.DisconnectNodeByAddr(peer.Addr())
×
258
        if err != nil {
×
259
                return nil, err
×
260
        }
×
261

262
        return &DisconnectPeerResponse{}, nil
×
263
}
264

265
// IsBanned returns true if the peer is banned, otherwise false.
266
//
267
// NOTE: Part of the NeutrinoKitServer interface.
268
func (s *Server) IsBanned(ctx context.Context,
269
        in *IsBannedRequest) (*IsBannedResponse, error) {
×
270

×
271
        if s.cfg.NeutrinoCS == nil {
×
272
                return nil, ErrNeutrinoNotActive
×
273
        }
×
274

275
        return &IsBannedResponse{
×
276
                Banned: s.cfg.NeutrinoCS.IsBanned(in.PeerAddrs),
×
277
        }, nil
×
278
}
279

280
// GetBlockHeader returns a block header with a particular block hash. If the
281
// block header is found in the cache, it will be returned immediately.
282
// Otherwise a block will  be requested from the network, one peer at a time,
283
// until one answers.
284
//
285
// NOTE: Part of the NeutrinoKitServer interface.
286
func (s *Server) GetBlockHeader(ctx context.Context,
287
        in *GetBlockHeaderRequest) (*GetBlockHeaderResponse, error) {
×
288

×
289
        if s.cfg.NeutrinoCS == nil {
×
290
                return nil, ErrNeutrinoNotActive
×
291
        }
×
292

293
        var hash chainhash.Hash
×
294
        if err := chainhash.Decode(&hash, in.Hash); err != nil {
×
295
                return nil, err
×
296
        }
×
297

298
        resp, err := s.getBlock(hash)
×
299
        if err != nil {
×
300
                return nil, err
×
301
        }
×
302

303
        return &GetBlockHeaderResponse{
×
304
                Hash:              resp.Hash,
×
305
                Confirmations:     resp.Confirmations,
×
306
                StrippedSize:      resp.StrippedSize,
×
307
                Size:              resp.Size,
×
308
                Weight:            resp.Weight,
×
309
                Height:            resp.Height,
×
310
                Version:           resp.Version,
×
311
                VersionHex:        resp.VersionHex,
×
312
                Merkleroot:        resp.Merkleroot,
×
313
                Time:              resp.Time,
×
314
                Nonce:             resp.Nonce,
×
315
                Bits:              resp.Bits,
×
316
                Ntx:               resp.Ntx,
×
317
                PreviousBlockHash: resp.PreviousBlockHash,
×
318
                RawHex:            resp.RawHex,
×
319
        }, nil
×
320
}
321

322
// GetBlock returns a block with a particular block hash. If the block is
323
// found in the cache, it will be returned immediately. Otherwise a block will
324
// be requested from the network, one peer at a time, until one answers.
325
//
326
// NOTE: Part of the NeutrinoKitServer interface.
327
func (s *Server) GetBlock(ctx context.Context,
328
        in *GetBlockRequest) (*GetBlockResponse, error) {
×
329

×
330
        if s.cfg.NeutrinoCS == nil {
×
331
                return nil, ErrNeutrinoNotActive
×
332
        }
×
333

334
        var hash chainhash.Hash
×
335
        if err := chainhash.Decode(&hash, in.Hash); err != nil {
×
336
                return nil, err
×
337
        }
×
338

339
        return s.getBlock(hash)
×
340
}
341

342
// GetCFilter returns a compact filter of a particular block.
343
// If found, only regular filters will be returned.
344
//
345
// NOTE: Part of the NeutrinoKitServer interface.
346
func (s *Server) GetCFilter(ctx context.Context,
347
        in *GetCFilterRequest) (*GetCFilterResponse, error) {
×
348

×
349
        if s.cfg.NeutrinoCS == nil {
×
350
                return nil, ErrNeutrinoNotActive
×
351
        }
×
352

353
        var hash chainhash.Hash
×
354
        if err := chainhash.Decode(&hash, in.Hash); err != nil {
×
355
                return nil, err
×
356
        }
×
357

358
        // GetCFilter returns a compact filter from the database. If it is
359
        // missing, it requests the compact filter from the network.
360
        filter, err := s.cfg.NeutrinoCS.GetCFilter(hash, wire.GCSFilterRegular)
×
361
        if err != nil {
×
362
                return nil, err
×
363
        }
×
364

365
        filterlBytes, err := filter.Bytes()
×
366
        if err != nil {
×
367
                return nil, err
×
368
        }
×
369

370
        return &GetCFilterResponse{Filter: filterlBytes}, nil
×
371
}
372

373
func (s *Server) getBlock(hash chainhash.Hash) (*GetBlockResponse, error) {
×
374
        block, err := s.cfg.NeutrinoCS.GetBlock(hash)
×
375
        if err != nil {
×
376
                return nil, err
×
377
        }
×
378

379
        header, _, err := s.cfg.NeutrinoCS.BlockHeaders.FetchHeader(&hash)
×
380
        if err != nil {
×
381
                return nil, err
×
382
        }
×
383

384
        blockData, err := block.Bytes()
×
385
        if err != nil {
×
386
                return nil, err
×
387
        }
×
388

389
        strippedData, err := block.BytesNoWitness()
×
390
        if err != nil {
×
391
                return nil, err
×
392
        }
×
393

394
        bestBlock, err := s.cfg.NeutrinoCS.BestBlock()
×
395
        if err != nil {
×
396
                return nil, err
×
397
        }
×
398

399
        // Convert txids to a string array.
400
        transactions := block.Transactions()
×
401
        tx := make([]string, len(transactions))
×
402
        for i := range transactions {
×
403
                tx[i] = transactions[i].Hash().String()
×
404
        }
×
405

406
        return &GetBlockResponse{
×
407
                Hash:          block.Hash().String(),
×
408
                Confirmations: int64(1 + bestBlock.Height - block.Height()),
×
409
                StrippedSize:  int64(len(strippedData)),
×
410
                Size:          int64(len(blockData)),
×
411
                Weight:        blockchain.GetBlockWeight(block),
×
412
                Height:        block.Height(),
×
413
                Version:       header.Version,
×
414
                VersionHex:    fmt.Sprintf("%0x", header.Version),
×
415
                Merkleroot:    header.MerkleRoot.String(),
×
416
                Tx:            tx,
×
417
                Time:          header.Timestamp.Unix(),
×
418
                Nonce:         header.Nonce,
×
419
                // Format bits as a hex.
×
420
                Bits:              fmt.Sprintf("%0x", header.Bits),
×
421
                Ntx:               int32(len(block.Transactions())),
×
422
                PreviousBlockHash: header.PrevBlock.String(),
×
423
                RawHex:            blockData,
×
424
        }, nil
×
425
}
426

427
// GetBlockHash returns the header hash of a block at a given height.
428
//
429
// NOTE: Part of the NeutrinoKitServer interface.
430
func (s *Server) GetBlockHash(ctx context.Context,
431
        in *GetBlockHashRequest) (*GetBlockHashResponse, error) {
×
432

×
433
        if s.cfg.NeutrinoCS == nil {
×
434
                return nil, ErrNeutrinoNotActive
×
435
        }
×
436

437
        hash, err := s.cfg.NeutrinoCS.GetBlockHash(int64(in.Height))
×
438
        if err != nil {
×
439
                return nil, err
×
440
        }
×
441

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