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

lightningnetwork / lnd / 13674605784

05 Mar 2025 11:09AM UTC coverage: 56.607% (-12.0%) from 68.655%
13674605784

Pull #9581

github

web-flow
Merge a116eef5e into 9feb761b4
Pull Request #9581: tor: fix `TestReconnectSucceed`

104674 of 184913 relevant lines covered (56.61%)

24195.64 hits per line

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

0.0
/lnrpc/routerrpc/forward_interceptor.go
1
package routerrpc
2

3
import (
4
        "errors"
5

6
        "github.com/lightningnetwork/lnd/fn/v2"
7
        "github.com/lightningnetwork/lnd/graph/db/models"
8
        "github.com/lightningnetwork/lnd/htlcswitch"
9
        "github.com/lightningnetwork/lnd/lnrpc"
10
        "github.com/lightningnetwork/lnd/lntypes"
11
        "github.com/lightningnetwork/lnd/lnwire"
12
        "google.golang.org/grpc/codes"
13
        "google.golang.org/grpc/status"
14
)
15

16
var (
17
        // ErrFwdNotExists is an error returned when the caller tries to resolve
18
        // a forward that doesn't exist anymore.
19
        ErrFwdNotExists = errors.New("forward does not exist")
20

21
        // ErrMissingPreimage is an error returned when the caller tries to settle
22
        // a forward and doesn't provide a preimage.
23
        ErrMissingPreimage = errors.New("missing preimage")
24
)
25

26
// forwardInterceptor is a helper struct that handles the lifecycle of an RPC
27
// interceptor streaming session.
28
// It is created when the stream opens and disconnects when the stream closes.
29
type forwardInterceptor struct {
30
        // stream is the bidirectional RPC stream
31
        stream Router_HtlcInterceptorServer
32

33
        htlcSwitch htlcswitch.InterceptableHtlcForwarder
34
}
35

36
// newForwardInterceptor creates a new forwardInterceptor.
37
func newForwardInterceptor(htlcSwitch htlcswitch.InterceptableHtlcForwarder,
38
        stream Router_HtlcInterceptorServer) *forwardInterceptor {
×
39

×
40
        return &forwardInterceptor{
×
41
                htlcSwitch: htlcSwitch,
×
42
                stream:     stream,
×
43
        }
×
44
}
×
45

46
// run sends the intercepted packets to the client and receives the
47
// corresponding responses. On one hand it registered itself as an interceptor
48
// that receives the switch packets and on the other hand launches a go routine
49
// to read from the client stream.
50
// To coordinate all this and make sure it is safe for concurrent access all
51
// packets are sent to the main where they are handled.
52
func (r *forwardInterceptor) run() error {
×
53
        // Register our interceptor so we receive all forwarded packets.
×
54
        r.htlcSwitch.SetInterceptor(r.onIntercept)
×
55
        defer r.htlcSwitch.SetInterceptor(nil)
×
56

×
57
        for {
×
58
                resp, err := r.stream.Recv()
×
59
                if err != nil {
×
60
                        return err
×
61
                }
×
62

63
                if err := r.resolveFromClient(resp); err != nil {
×
64
                        return err
×
65
                }
×
66
        }
67
}
68

69
// onIntercept is the function that is called by the switch for every forwarded
70
// packet. Our interceptor makes sure we hold the packet and then signal to the
71
// main loop to handle the packet. We only return true if we were able
72
// to deliver the packet to the main loop.
73
func (r *forwardInterceptor) onIntercept(
74
        htlc htlcswitch.InterceptedPacket) error {
×
75

×
76
        log.Tracef("Sending intercepted packet to client %v", htlc)
×
77

×
78
        inKey := htlc.IncomingCircuit
×
79

×
80
        // First hold the forward, then send to client.
×
81
        interceptionRequest := &ForwardHtlcInterceptRequest{
×
82
                IncomingCircuitKey: &CircuitKey{
×
83
                        ChanId: inKey.ChanID.ToUint64(),
×
84
                        HtlcId: inKey.HtlcID,
×
85
                },
×
86
                OutgoingRequestedChanId: htlc.OutgoingChanID.ToUint64(),
×
87
                PaymentHash:             htlc.Hash[:],
×
88
                OutgoingAmountMsat:      uint64(htlc.OutgoingAmount),
×
89
                OutgoingExpiry:          htlc.OutgoingExpiry,
×
90
                IncomingAmountMsat:      uint64(htlc.IncomingAmount),
×
91
                IncomingExpiry:          htlc.IncomingExpiry,
×
92
                CustomRecords:           htlc.InOnionCustomRecords,
×
93
                OnionBlob:               htlc.OnionBlob[:],
×
94
                AutoFailHeight:          htlc.AutoFailHeight,
×
95
                InWireCustomRecords:     htlc.InWireCustomRecords,
×
96
        }
×
97

×
98
        return r.stream.Send(interceptionRequest)
×
99
}
×
100

101
// resolveFromClient handles a resolution arrived from the client.
102
func (r *forwardInterceptor) resolveFromClient(
103
        in *ForwardHtlcInterceptResponse) error {
×
104

×
105
        if in.IncomingCircuitKey == nil {
×
106
                return status.Errorf(codes.InvalidArgument,
×
107
                        "CircuitKey missing from ForwardHtlcInterceptResponse")
×
108
        }
×
109

110
        log.Tracef("Resolving intercepted packet %v", in)
×
111

×
112
        circuitKey := models.CircuitKey{
×
113
                ChanID: lnwire.NewShortChanIDFromInt(
×
114
                        in.IncomingCircuitKey.ChanId,
×
115
                ),
×
116
                HtlcID: in.IncomingCircuitKey.HtlcId,
×
117
        }
×
118

×
119
        switch in.Action {
×
120
        case ResolveHoldForwardAction_RESUME:
×
121
                return r.htlcSwitch.Resolve(&htlcswitch.FwdResolution{
×
122
                        Key:    circuitKey,
×
123
                        Action: htlcswitch.FwdActionResume,
×
124
                })
×
125

126
        case ResolveHoldForwardAction_RESUME_MODIFIED:
×
127
                // Modify HTLC and resume forward.
×
128
                inAmtMsat := fn.None[lnwire.MilliSatoshi]()
×
129
                if in.InAmountMsat > 0 {
×
130
                        inAmtMsat = fn.Some(lnwire.MilliSatoshi(
×
131
                                in.InAmountMsat,
×
132
                        ))
×
133
                }
×
134

135
                outAmtMsat := fn.None[lnwire.MilliSatoshi]()
×
136
                if in.OutAmountMsat > 0 {
×
137
                        outAmtMsat = fn.Some(lnwire.MilliSatoshi(
×
138
                                in.OutAmountMsat,
×
139
                        ))
×
140
                }
×
141

142
                outWireCustomRecords := fn.None[lnwire.CustomRecords]()
×
143
                if len(in.OutWireCustomRecords) > 0 {
×
144
                        // Validate custom records.
×
145
                        cr := lnwire.CustomRecords(in.OutWireCustomRecords)
×
146
                        if err := cr.Validate(); err != nil {
×
147
                                return status.Errorf(
×
148
                                        codes.InvalidArgument,
×
149
                                        "failed to validate custom records: %v",
×
150
                                        err,
×
151
                                )
×
152
                        }
×
153

154
                        outWireCustomRecords = fn.Some[lnwire.CustomRecords](cr)
×
155
                }
156

157
                //nolint:ll
158
                return r.htlcSwitch.Resolve(&htlcswitch.FwdResolution{
×
159
                        Key:                  circuitKey,
×
160
                        Action:               htlcswitch.FwdActionResumeModified,
×
161
                        InAmountMsat:         inAmtMsat,
×
162
                        OutAmountMsat:        outAmtMsat,
×
163
                        OutWireCustomRecords: outWireCustomRecords,
×
164
                })
×
165

166
        case ResolveHoldForwardAction_FAIL:
×
167
                // Fail with an encrypted reason.
×
168
                if in.FailureMessage != nil {
×
169
                        if in.FailureCode != 0 {
×
170
                                return status.Errorf(
×
171
                                        codes.InvalidArgument,
×
172
                                        "failure message and failure code "+
×
173
                                                "are mutually exclusive",
×
174
                                )
×
175
                        }
×
176

177
                        // Verify that the size is equal to the fixed failure
178
                        // message size + hmac + two uint16 lengths. See BOLT
179
                        // #4.
180
                        if len(in.FailureMessage) !=
×
181
                                lnwire.FailureMessageLength+32+2+2 {
×
182

×
183
                                return status.Errorf(
×
184
                                        codes.InvalidArgument,
×
185
                                        "failure message length invalid",
×
186
                                )
×
187
                        }
×
188

189
                        return r.htlcSwitch.Resolve(&htlcswitch.FwdResolution{
×
190
                                Key:            circuitKey,
×
191
                                Action:         htlcswitch.FwdActionFail,
×
192
                                FailureMessage: in.FailureMessage,
×
193
                        })
×
194
                }
195

196
                var code lnwire.FailCode
×
197
                switch in.FailureCode {
×
198
                case lnrpc.Failure_INVALID_ONION_HMAC:
×
199
                        code = lnwire.CodeInvalidOnionHmac
×
200

201
                case lnrpc.Failure_INVALID_ONION_KEY:
×
202
                        code = lnwire.CodeInvalidOnionKey
×
203

204
                case lnrpc.Failure_INVALID_ONION_VERSION:
×
205
                        code = lnwire.CodeInvalidOnionVersion
×
206

207
                // Default to TemporaryChannelFailure.
208
                case 0, lnrpc.Failure_TEMPORARY_CHANNEL_FAILURE:
×
209
                        code = lnwire.CodeTemporaryChannelFailure
×
210

211
                default:
×
212
                        return status.Errorf(
×
213
                                codes.InvalidArgument,
×
214
                                "unsupported failure code: %v", in.FailureCode,
×
215
                        )
×
216
                }
217

218
                return r.htlcSwitch.Resolve(&htlcswitch.FwdResolution{
×
219
                        Key:         circuitKey,
×
220
                        Action:      htlcswitch.FwdActionFail,
×
221
                        FailureCode: code,
×
222
                })
×
223

224
        case ResolveHoldForwardAction_SETTLE:
×
225
                if in.Preimage == nil {
×
226
                        return ErrMissingPreimage
×
227
                }
×
228
                preimage, err := lntypes.MakePreimage(in.Preimage)
×
229
                if err != nil {
×
230
                        return err
×
231
                }
×
232

233
                return r.htlcSwitch.Resolve(&htlcswitch.FwdResolution{
×
234
                        Key:      circuitKey,
×
235
                        Action:   htlcswitch.FwdActionSettle,
×
236
                        Preimage: preimage,
×
237
                })
×
238

239
        default:
×
240
                return status.Errorf(
×
241
                        codes.InvalidArgument,
×
242
                        "unrecognized resolve action %v", in.Action,
×
243
                )
×
244
        }
245
}
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