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

lightningnetwork / lnd / 11170835610

03 Oct 2024 10:41PM UTC coverage: 49.188% (-9.6%) from 58.738%
11170835610

push

github

web-flow
Merge pull request #9154 from ziggie1984/master

multi: bump btcd version.

3 of 6 new or added lines in 6 files covered. (50.0%)

26110 existing lines in 428 files now uncovered.

97359 of 197934 relevant lines covered (49.19%)

1.04 hits per line

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

75.87
/chanacceptor/rpcacceptor.go
1
package chanacceptor
2

3
import (
4
        "encoding/hex"
5
        "errors"
6
        "fmt"
7
        "sync"
8
        "time"
9

10
        "github.com/btcsuite/btcd/btcutil"
11
        "github.com/btcsuite/btcd/chaincfg"
12
        "github.com/lightningnetwork/lnd/input"
13
        "github.com/lightningnetwork/lnd/lnrpc"
14
        "github.com/lightningnetwork/lnd/lnwallet/chancloser"
15
        "github.com/lightningnetwork/lnd/lnwire"
16
)
17

18
var (
19
        errShuttingDown = errors.New("server shutting down")
20

21
        // errCustomLength is returned when our custom error's length exceeds
22
        // our maximum.
23
        errCustomLength = fmt.Errorf("custom error message exceeds length "+
24
                "limit: %v", maxErrorLength)
25

26
        // errInvalidUpfrontShutdown is returned when we cannot parse the
27
        // upfront shutdown address returned.
28
        errInvalidUpfrontShutdown = fmt.Errorf("could not parse upfront " +
29
                "shutdown address")
30

31
        // errInsufficientReserve is returned when the reserve proposed by for
32
        // a channel is less than the dust limit originally supplied.
33
        errInsufficientReserve = fmt.Errorf("reserve lower than proposed dust " +
34
                "limit")
35

36
        // errAcceptWithError is returned when we get a response which accepts
37
        // a channel but ambiguously also sets a custom error message.
38
        errAcceptWithError = errors.New("channel acceptor response accepts " +
39
                "channel, but also includes custom error")
40

41
        // errMaxHtlcTooHigh is returned if our htlc count exceeds the number
42
        // hard-set by BOLT 2.
43
        errMaxHtlcTooHigh = fmt.Errorf("htlc limit exceeds spec limit of: %v",
44
                input.MaxHTLCNumber/2)
45

46
        // maxErrorLength is the maximum error length we allow the error we
47
        // send to our peer to be.
48
        maxErrorLength = 500
49
)
50

51
// chanAcceptInfo contains a request for a channel acceptor decision, and a
52
// channel that the response should be sent on.
53
type chanAcceptInfo struct {
54
        request  *ChannelAcceptRequest
55
        response chan *ChannelAcceptResponse
56
}
57

58
// RPCAcceptor represents the RPC-controlled variant of the ChannelAcceptor.
59
// One RPCAcceptor allows one RPC client.
60
type RPCAcceptor struct {
61
        // receive is a function from which we receive channel acceptance
62
        // decisions. Note that this function is expected to block.
63
        receive func() (*lnrpc.ChannelAcceptResponse, error)
64

65
        // send is a function which sends requests for channel acceptance
66
        // decisions into our rpc stream.
67
        send func(request *lnrpc.ChannelAcceptRequest) error
68

69
        // requests is a channel that we send requests for a acceptor response
70
        // into.
71
        requests chan *chanAcceptInfo
72

73
        // timeout is the amount of time we allow the channel acceptance
74
        // decision to take. This time includes the time to send a query to the
75
        // acceptor, and the time it takes to receive a response.
76
        timeout time.Duration
77

78
        // params are our current chain params.
79
        params *chaincfg.Params
80

81
        // done is closed when the rpc client terminates.
82
        done chan struct{}
83

84
        // quit is closed when lnd is shutting down.
85
        quit chan struct{}
86

87
        wg sync.WaitGroup
88
}
89

90
// Accept is a predicate on the ChannelAcceptRequest which is sent to the RPC
91
// client who will respond with the ultimate decision. This function passes the
92
// request into the acceptor's requests channel, and returns the response it
93
// receives, failing the request if the timeout elapses.
94
//
95
// NOTE: Part of the ChannelAcceptor interface.
96
func (r *RPCAcceptor) Accept(req *ChannelAcceptRequest) *ChannelAcceptResponse {
2✔
97
        respChan := make(chan *ChannelAcceptResponse, 1)
2✔
98

2✔
99
        newRequest := &chanAcceptInfo{
2✔
100
                request:  req,
2✔
101
                response: respChan,
2✔
102
        }
2✔
103

2✔
104
        // timeout is the time after which ChannelAcceptRequests expire.
2✔
105
        timeout := time.After(r.timeout)
2✔
106

2✔
107
        // Create a rejection response which we can use for the cases where we
2✔
108
        // reject the channel.
2✔
109
        rejectChannel := NewChannelAcceptResponse(
2✔
110
                false, errChannelRejected, nil, 0, 0, 0, 0, 0, 0, false,
2✔
111
        )
2✔
112

2✔
113
        // Send the request to the newRequests channel.
2✔
114
        select {
2✔
115
        case r.requests <- newRequest:
2✔
116

117
        case <-timeout:
×
118
                log.Errorf("RPCAcceptor returned false - reached timeout of %v",
×
119
                        r.timeout)
×
120
                return rejectChannel
×
121

122
        case <-r.done:
×
123
                return rejectChannel
×
124

125
        case <-r.quit:
×
126
                return rejectChannel
×
127
        }
128

129
        // Receive the response and return it. If no response has been received
130
        // in AcceptorTimeout, then return false.
131
        select {
2✔
132
        case resp := <-respChan:
2✔
133
                return resp
2✔
134

135
        case <-timeout:
×
136
                log.Errorf("RPCAcceptor returned false - reached timeout of %v",
×
137
                        r.timeout)
×
138
                return rejectChannel
×
139

140
        case <-r.done:
×
141
                return rejectChannel
×
142

143
        case <-r.quit:
×
144
                return rejectChannel
×
145
        }
146
}
147

148
// NewRPCAcceptor creates and returns an instance of the RPCAcceptor.
149
func NewRPCAcceptor(receive func() (*lnrpc.ChannelAcceptResponse, error),
150
        send func(*lnrpc.ChannelAcceptRequest) error, timeout time.Duration,
151
        params *chaincfg.Params, quit chan struct{}) *RPCAcceptor {
2✔
152

2✔
153
        return &RPCAcceptor{
2✔
154
                receive:  receive,
2✔
155
                send:     send,
2✔
156
                requests: make(chan *chanAcceptInfo),
2✔
157
                timeout:  timeout,
2✔
158
                params:   params,
2✔
159
                done:     make(chan struct{}),
2✔
160
                quit:     quit,
2✔
161
        }
2✔
162
}
2✔
163

164
// Run is the main loop for the RPC Acceptor. This function will block until
165
// it receives the signal that lnd is shutting down, or the rpc stream is
166
// cancelled by the client.
167
func (r *RPCAcceptor) Run() error {
2✔
168
        // Wait for our goroutines to exit before we return.
2✔
169
        defer r.wg.Wait()
2✔
170

2✔
171
        // Create a channel that responses from acceptors are sent into.
2✔
172
        responses := make(chan *lnrpc.ChannelAcceptResponse)
2✔
173

2✔
174
        // errChan is used by the receive loop to signal any errors that occur
2✔
175
        // during reading from the stream. This is primarily used to shutdown
2✔
176
        // the send loop in the case of an RPC client disconnecting.
2✔
177
        errChan := make(chan error, 1)
2✔
178

2✔
179
        // Start a goroutine to receive responses from the channel acceptor.
2✔
180
        // We expect the receive function to block, so it must be run in a
2✔
181
        // goroutine (otherwise we could not send more than one channel accept
2✔
182
        // request to the client).
2✔
183
        r.wg.Add(1)
2✔
184
        go func() {
4✔
185
                r.receiveResponses(errChan, responses)
2✔
186
                r.wg.Done()
2✔
187
        }()
2✔
188

189
        return r.sendAcceptRequests(errChan, responses)
2✔
190
}
191

192
// receiveResponses receives responses for our channel accept requests and
193
// dispatches them into the responses channel provided, sending any errors that
194
// occur into the error channel provided.
195
func (r *RPCAcceptor) receiveResponses(errChan chan error,
196
        responses chan *lnrpc.ChannelAcceptResponse) {
2✔
197

2✔
198
        for {
4✔
199
                resp, err := r.receive()
2✔
200
                if err != nil {
4✔
201
                        errChan <- err
2✔
202
                        return
2✔
203
                }
2✔
204

205
                var pendingID [32]byte
2✔
206
                copy(pendingID[:], resp.PendingChanId)
2✔
207

2✔
208
                openChanResp := &lnrpc.ChannelAcceptResponse{
2✔
209
                        Accept:          resp.Accept,
2✔
210
                        PendingChanId:   pendingID[:],
2✔
211
                        Error:           resp.Error,
2✔
212
                        UpfrontShutdown: resp.UpfrontShutdown,
2✔
213
                        CsvDelay:        resp.CsvDelay,
2✔
214
                        ReserveSat:      resp.ReserveSat,
2✔
215
                        InFlightMaxMsat: resp.InFlightMaxMsat,
2✔
216
                        MaxHtlcCount:    resp.MaxHtlcCount,
2✔
217
                        MinHtlcIn:       resp.MinHtlcIn,
2✔
218
                        MinAcceptDepth:  resp.MinAcceptDepth,
2✔
219
                        ZeroConf:        resp.ZeroConf,
2✔
220
                }
2✔
221

2✔
222
                // We have received a decision for one of our channel
2✔
223
                // acceptor requests.
2✔
224
                select {
2✔
225
                case responses <- openChanResp:
2✔
226

227
                case <-r.done:
×
228
                        return
×
229

230
                case <-r.quit:
×
231
                        return
×
232
                }
233
        }
234
}
235

236
// sendAcceptRequests handles channel acceptor requests sent to us by our
237
// Accept() function, dispatching them to our acceptor stream and coordinating
238
// return of responses to their callers.
239
func (r *RPCAcceptor) sendAcceptRequests(errChan chan error,
240
        responses chan *lnrpc.ChannelAcceptResponse) error {
2✔
241

2✔
242
        // Close the done channel to indicate that the acceptor is no longer
2✔
243
        // listening and any in-progress requests should be terminated.
2✔
244
        defer close(r.done)
2✔
245

2✔
246
        // Create a map of pending channel IDs to our original open channel
2✔
247
        // request and a response channel. We keep the original channel open
2✔
248
        // message so that we can validate our response against it.
2✔
249
        acceptRequests := make(map[[32]byte]*chanAcceptInfo)
2✔
250

2✔
251
        for {
4✔
252
                //nolint:lll
2✔
253
                select {
2✔
254
                // Consume requests passed to us from our Accept() function and
255
                // send them into our stream.
256
                case newRequest := <-r.requests:
2✔
257

2✔
258
                        req := newRequest.request
2✔
259
                        pendingChanID := req.OpenChanMsg.PendingChannelID
2✔
260

2✔
261
                        // Map the channel commitment type to its RPC
2✔
262
                        // counterpart. Also determine whether the zero-conf or
2✔
263
                        // scid-alias channel types are set.
2✔
264
                        var (
2✔
265
                                commitmentType lnrpc.CommitmentType
2✔
266
                                wantsZeroConf  bool
2✔
267
                                wantsScidAlias bool
2✔
268
                        )
2✔
269

2✔
270
                        if req.OpenChanMsg.ChannelType != nil {
4✔
271
                                channelFeatures := lnwire.RawFeatureVector(
2✔
272
                                        *req.OpenChanMsg.ChannelType,
2✔
273
                                )
2✔
274
                                switch {
2✔
275
                                case channelFeatures.OnlyContains(
276
                                        lnwire.ZeroConfRequired,
277
                                        lnwire.ScidAliasRequired,
278
                                        lnwire.ScriptEnforcedLeaseRequired,
279
                                        lnwire.AnchorsZeroFeeHtlcTxRequired,
280
                                        lnwire.StaticRemoteKeyRequired,
281
                                ):
×
282
                                        commitmentType = lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE
×
283

284
                                case channelFeatures.OnlyContains(
285
                                        lnwire.ZeroConfRequired,
286
                                        lnwire.ScriptEnforcedLeaseRequired,
287
                                        lnwire.AnchorsZeroFeeHtlcTxRequired,
288
                                        lnwire.StaticRemoteKeyRequired,
289
                                ):
2✔
290
                                        commitmentType = lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE
2✔
291

292
                                case channelFeatures.OnlyContains(
293
                                        lnwire.ScidAliasRequired,
294
                                        lnwire.ScriptEnforcedLeaseRequired,
295
                                        lnwire.AnchorsZeroFeeHtlcTxRequired,
296
                                        lnwire.StaticRemoteKeyRequired,
297
                                ):
×
298
                                        commitmentType = lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE
×
299

300
                                case channelFeatures.OnlyContains(
301
                                        lnwire.ScriptEnforcedLeaseRequired,
302
                                        lnwire.AnchorsZeroFeeHtlcTxRequired,
303
                                        lnwire.StaticRemoteKeyRequired,
304
                                ):
×
305
                                        commitmentType = lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE
×
306

307
                                case channelFeatures.OnlyContains(
308
                                        lnwire.ZeroConfRequired,
309
                                        lnwire.ScidAliasRequired,
310
                                        lnwire.AnchorsZeroFeeHtlcTxRequired,
311
                                        lnwire.StaticRemoteKeyRequired,
312
                                ):
×
313
                                        commitmentType = lnrpc.CommitmentType_ANCHORS
×
314

315
                                case channelFeatures.OnlyContains(
316
                                        lnwire.ZeroConfRequired,
317
                                        lnwire.AnchorsZeroFeeHtlcTxRequired,
318
                                        lnwire.StaticRemoteKeyRequired,
319
                                ):
2✔
320
                                        commitmentType = lnrpc.CommitmentType_ANCHORS
2✔
321

322
                                case channelFeatures.OnlyContains(
323
                                        lnwire.ScidAliasRequired,
324
                                        lnwire.AnchorsZeroFeeHtlcTxRequired,
325
                                        lnwire.StaticRemoteKeyRequired,
326
                                ):
2✔
327
                                        commitmentType = lnrpc.CommitmentType_ANCHORS
2✔
328

329
                                case channelFeatures.OnlyContains(
330
                                        lnwire.AnchorsZeroFeeHtlcTxRequired,
331
                                        lnwire.StaticRemoteKeyRequired,
332
                                ):
2✔
333
                                        commitmentType = lnrpc.CommitmentType_ANCHORS
2✔
334

335
                                case channelFeatures.OnlyContains(
336
                                        lnwire.SimpleTaprootChannelsRequiredStaging,
337
                                        lnwire.ZeroConfRequired,
338
                                        lnwire.ScidAliasRequired,
339
                                ):
×
340
                                        commitmentType = lnrpc.CommitmentType_SIMPLE_TAPROOT
×
341

342
                                case channelFeatures.OnlyContains(
343
                                        lnwire.SimpleTaprootChannelsRequiredStaging,
344
                                        lnwire.ZeroConfRequired,
345
                                ):
2✔
346
                                        commitmentType = lnrpc.CommitmentType_SIMPLE_TAPROOT
2✔
347

348
                                case channelFeatures.OnlyContains(
349
                                        lnwire.SimpleTaprootChannelsRequiredStaging,
350
                                        lnwire.ScidAliasRequired,
351
                                ):
×
352
                                        commitmentType = lnrpc.CommitmentType_SIMPLE_TAPROOT
×
353

354
                                case channelFeatures.OnlyContains(
355
                                        lnwire.SimpleTaprootChannelsRequiredStaging,
356
                                ):
×
357
                                        commitmentType = lnrpc.CommitmentType_SIMPLE_TAPROOT
×
358

359
                                case channelFeatures.OnlyContains(
360
                                        lnwire.StaticRemoteKeyRequired,
361
                                ):
2✔
362
                                        commitmentType = lnrpc.CommitmentType_STATIC_REMOTE_KEY
2✔
363

364
                                case channelFeatures.OnlyContains():
×
365
                                        commitmentType = lnrpc.CommitmentType_LEGACY
×
366

367
                                default:
×
368
                                        log.Warnf("Unhandled commitment type "+
×
369
                                                "in channel acceptor request: %v",
×
370
                                                req.OpenChanMsg.ChannelType)
×
371
                                }
372

373
                                if channelFeatures.IsSet(
2✔
374
                                        lnwire.ZeroConfRequired,
2✔
375
                                ) {
4✔
376

2✔
377
                                        wantsZeroConf = true
2✔
378
                                }
2✔
379

380
                                if channelFeatures.IsSet(
2✔
381
                                        lnwire.ScidAliasRequired,
2✔
382
                                ) {
4✔
383

2✔
384
                                        wantsScidAlias = true
2✔
385
                                }
2✔
386
                        }
387

388
                        acceptRequests[pendingChanID] = newRequest
2✔
389

2✔
390
                        // A ChannelAcceptRequest has been received, send it to the client.
2✔
391
                        chanAcceptReq := &lnrpc.ChannelAcceptRequest{
2✔
392
                                NodePubkey:       req.Node.SerializeCompressed(),
2✔
393
                                ChainHash:        req.OpenChanMsg.ChainHash[:],
2✔
394
                                PendingChanId:    req.OpenChanMsg.PendingChannelID[:],
2✔
395
                                FundingAmt:       uint64(req.OpenChanMsg.FundingAmount),
2✔
396
                                PushAmt:          uint64(req.OpenChanMsg.PushAmount),
2✔
397
                                DustLimit:        uint64(req.OpenChanMsg.DustLimit),
2✔
398
                                MaxValueInFlight: uint64(req.OpenChanMsg.MaxValueInFlight),
2✔
399
                                ChannelReserve:   uint64(req.OpenChanMsg.ChannelReserve),
2✔
400
                                MinHtlc:          uint64(req.OpenChanMsg.HtlcMinimum),
2✔
401
                                FeePerKw:         uint64(req.OpenChanMsg.FeePerKiloWeight),
2✔
402
                                CsvDelay:         uint32(req.OpenChanMsg.CsvDelay),
2✔
403
                                MaxAcceptedHtlcs: uint32(req.OpenChanMsg.MaxAcceptedHTLCs),
2✔
404
                                ChannelFlags:     uint32(req.OpenChanMsg.ChannelFlags),
2✔
405
                                CommitmentType:   commitmentType,
2✔
406
                                WantsZeroConf:    wantsZeroConf,
2✔
407
                                WantsScidAlias:   wantsScidAlias,
2✔
408
                        }
2✔
409

2✔
410
                        if err := r.send(chanAcceptReq); err != nil {
2✔
411
                                return err
×
412
                        }
×
413

414
                // Process newly received responses from our channel acceptor,
415
                // looking the original request up in our map of requests and
416
                // dispatching the response.
417
                case resp := <-responses:
2✔
418
                        // Look up the appropriate channel to send on given the
2✔
419
                        // pending ID. If a channel is found, send the response
2✔
420
                        // over it.
2✔
421
                        var pendingID [32]byte
2✔
422
                        copy(pendingID[:], resp.PendingChanId)
2✔
423
                        requestInfo, ok := acceptRequests[pendingID]
2✔
424
                        if !ok {
4✔
425
                                continue
2✔
426
                        }
427

428
                        // Validate the response we have received. If it is not
429
                        // valid, we log our error and proceed to deliver the
430
                        // rejection.
431
                        accept, acceptErr, shutdown, err := r.validateAcceptorResponse(
2✔
432
                                requestInfo.request.OpenChanMsg.DustLimit, resp,
2✔
433
                        )
2✔
434
                        if err != nil {
2✔
UNCOV
435
                                log.Errorf("Invalid acceptor response: %v", err)
×
UNCOV
436
                        }
×
437

438
                        requestInfo.response <- NewChannelAcceptResponse(
2✔
439
                                accept, acceptErr, shutdown,
2✔
440
                                uint16(resp.CsvDelay),
2✔
441
                                uint16(resp.MaxHtlcCount),
2✔
442
                                uint16(resp.MinAcceptDepth),
2✔
443
                                btcutil.Amount(resp.ReserveSat),
2✔
444
                                lnwire.MilliSatoshi(resp.InFlightMaxMsat),
2✔
445
                                lnwire.MilliSatoshi(resp.MinHtlcIn),
2✔
446
                                resp.ZeroConf,
2✔
447
                        )
2✔
448

2✔
449
                        // Delete the channel from the acceptRequests map.
2✔
450
                        delete(acceptRequests, pendingID)
2✔
451

452
                // If we failed to receive from our acceptor, we exit.
453
                case err := <-errChan:
2✔
454
                        log.Errorf("Received an error: %v, shutting down", err)
2✔
455
                        return err
2✔
456

457
                // Exit if we are shutting down.
UNCOV
458
                case <-r.quit:
×
UNCOV
459
                        return errShuttingDown
×
460
                }
461
        }
462
}
463

464
// validateAcceptorResponse validates the response we get from the channel
465
// acceptor, returning a boolean indicating whether to accept the channel, an
466
// error to send to the peer, and any validation errors that occurred.
467
func (r *RPCAcceptor) validateAcceptorResponse(dustLimit btcutil.Amount,
468
        req *lnrpc.ChannelAcceptResponse) (bool, error, lnwire.DeliveryAddress,
469
        error) {
2✔
470

2✔
471
        channelStr := hex.EncodeToString(req.PendingChanId)
2✔
472

2✔
473
        // Check that the max htlc count is within the BOLT 2 hard-limit of 483.
2✔
474
        // The initiating side should fail values above this anyway, but we
2✔
475
        // catch the invalid user input here.
2✔
476
        if req.MaxHtlcCount > input.MaxHTLCNumber/2 {
2✔
UNCOV
477
                log.Errorf("Max htlc count: %v for channel: %v is greater "+
×
UNCOV
478
                        "than limit of: %v", req.MaxHtlcCount, channelStr,
×
UNCOV
479
                        input.MaxHTLCNumber/2)
×
UNCOV
480

×
UNCOV
481
                return false, errChannelRejected, nil, errMaxHtlcTooHigh
×
UNCOV
482
        }
×
483

484
        // Ensure that the reserve that has been proposed, if it is set, is at
485
        // least the dust limit that was proposed by the remote peer. This is
486
        // required by BOLT 2.
487
        reserveSat := btcutil.Amount(req.ReserveSat)
2✔
488
        if reserveSat != 0 && reserveSat < dustLimit {
2✔
UNCOV
489
                log.Errorf("Remote reserve: %v sat for channel: %v must be "+
×
UNCOV
490
                        "at least equal to proposed dust limit: %v",
×
UNCOV
491
                        req.ReserveSat, channelStr, dustLimit)
×
UNCOV
492

×
UNCOV
493
                return false, errChannelRejected, nil, errInsufficientReserve
×
UNCOV
494
        }
×
495

496
        // Attempt to parse the upfront shutdown address provided.
497
        upfront, err := chancloser.ParseUpfrontShutdownAddress(
2✔
498
                req.UpfrontShutdown, r.params,
2✔
499
        )
2✔
500
        if err != nil {
2✔
UNCOV
501
                log.Errorf("Could not parse upfront shutdown for "+
×
UNCOV
502
                        "%v: %v", channelStr, err)
×
UNCOV
503

×
UNCOV
504
                return false, errChannelRejected, nil, errInvalidUpfrontShutdown
×
UNCOV
505
        }
×
506

507
        // Check that the custom error provided is valid.
508
        if len(req.Error) > maxErrorLength {
2✔
UNCOV
509
                return false, errChannelRejected, nil, errCustomLength
×
UNCOV
510
        }
×
511

512
        var haveCustomError = len(req.Error) != 0
2✔
513

2✔
514
        switch {
2✔
515
        // If accept is true, but we also have an error specified, we fail
516
        // because this result is ambiguous.
UNCOV
517
        case req.Accept && haveCustomError:
×
UNCOV
518
                return false, errChannelRejected, nil, errAcceptWithError
×
519

520
        // If we accept without an error message, we can just return a nil
521
        // error.
522
        case req.Accept:
2✔
523
                return true, nil, upfront, nil
2✔
524

525
        // If we reject the channel, and have a custom error, then we use it.
UNCOV
526
        case haveCustomError:
×
UNCOV
527
                return false, fmt.Errorf(req.Error), nil, nil
×
528

529
        // Otherwise, we have rejected the channel with no custom error, so we
530
        // just use a generic error to fail the channel.
531
        default:
2✔
532
                return false, errChannelRejected, nil, nil
2✔
533
        }
534
}
535

536
// A compile-time constraint to ensure RPCAcceptor implements the ChannelAcceptor
537
// interface.
538
var _ ChannelAcceptor = (*RPCAcceptor)(nil)
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