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

lightningnetwork / lnd / 13035292482

29 Jan 2025 03:59PM UTC coverage: 49.3% (-9.5%) from 58.777%
13035292482

Pull #9456

github

mohamedawnallah
docs: update release-notes-0.19.0.md

In this commit, we warn users about the removal
of RPCs `SendToRoute`, `SendToRouteSync`, `SendPayment`,
and `SendPaymentSync` in the next release 0.20.
Pull Request #9456: lnrpc+docs: deprecate warning `SendToRoute`, `SendToRouteSync`, `SendPayment`, and `SendPaymentSync` in Release 0.19

100634 of 204126 relevant lines covered (49.3%)

1.54 hits per line

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

0.0
/channeldb/migration_01_to_11/route.go
1
package migration_01_to_11
2

3
import (
4
        "bytes"
5
        "encoding/binary"
6
        "encoding/hex"
7
        "fmt"
8
        "io"
9
        "strconv"
10
        "strings"
11

12
        "github.com/btcsuite/btcd/btcec/v2"
13
        sphinx "github.com/lightningnetwork/lightning-onion"
14
        lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21"
15
        "github.com/lightningnetwork/lnd/record"
16
        "github.com/lightningnetwork/lnd/tlv"
17
)
18

19
// VertexSize is the size of the array to store a vertex.
20
const VertexSize = 33
21

22
// ErrNoRouteHopsProvided is returned when a caller attempts to construct a new
23
// sphinx packet, but provides an empty set of hops for each route.
24
var ErrNoRouteHopsProvided = fmt.Errorf("empty route hops provided")
25

26
// Vertex is a simple alias for the serialization of a compressed Bitcoin
27
// public key.
28
type Vertex [VertexSize]byte
29

30
// NewVertex returns a new Vertex given a public key.
31
func NewVertex(pub *btcec.PublicKey) Vertex {
×
32
        var v Vertex
×
33
        copy(v[:], pub.SerializeCompressed())
×
34
        return v
×
35
}
×
36

37
// NewVertexFromBytes returns a new Vertex based on a serialized pubkey in a
38
// byte slice.
39
func NewVertexFromBytes(b []byte) (Vertex, error) {
×
40
        vertexLen := len(b)
×
41
        if vertexLen != VertexSize {
×
42
                return Vertex{}, fmt.Errorf("invalid vertex length of %v, "+
×
43
                        "want %v", vertexLen, VertexSize)
×
44
        }
×
45

46
        var v Vertex
×
47
        copy(v[:], b)
×
48
        return v, nil
×
49
}
50

51
// NewVertexFromStr returns a new Vertex given its hex-encoded string format.
52
func NewVertexFromStr(v string) (Vertex, error) {
×
53
        // Return error if hex string is of incorrect length.
×
54
        if len(v) != VertexSize*2 {
×
55
                return Vertex{}, fmt.Errorf("invalid vertex string length of "+
×
56
                        "%v, want %v", len(v), VertexSize*2)
×
57
        }
×
58

59
        vertex, err := hex.DecodeString(v)
×
60
        if err != nil {
×
61
                return Vertex{}, err
×
62
        }
×
63

64
        return NewVertexFromBytes(vertex)
×
65
}
66

67
// String returns a human readable version of the Vertex which is the
68
// hex-encoding of the serialized compressed public key.
69
func (v Vertex) String() string {
×
70
        return fmt.Sprintf("%x", v[:])
×
71
}
×
72

73
// Hop represents an intermediate or final node of the route. This naming
74
// is in line with the definition given in BOLT #4: Onion Routing Protocol.
75
// The struct houses the channel along which this hop can be reached and
76
// the values necessary to create the HTLC that needs to be sent to the
77
// next hop. It is also used to encode the per-hop payload included within
78
// the Sphinx packet.
79
type Hop struct {
80
        // PubKeyBytes is the raw bytes of the public key of the target node.
81
        PubKeyBytes Vertex
82

83
        // ChannelID is the unique channel ID for the channel. The first 3
84
        // bytes are the block height, the next 3 the index within the block,
85
        // and the last 2 bytes are the output index for the channel.
86
        ChannelID uint64
87

88
        // OutgoingTimeLock is the timelock value that should be used when
89
        // crafting the _outgoing_ HTLC from this hop.
90
        OutgoingTimeLock uint32
91

92
        // AmtToForward is the amount that this hop will forward to the next
93
        // hop. This value is less than the value that the incoming HTLC
94
        // carries as a fee will be subtracted by the hop.
95
        AmtToForward lnwire.MilliSatoshi
96

97
        // TLVRecords if non-nil are a set of additional TLV records that
98
        // should be included in the forwarding instructions for this node.
99
        TLVRecords []tlv.Record
100

101
        // LegacyPayload if true, then this signals that this node doesn't
102
        // understand the new TLV payload, so we must instead use the legacy
103
        // payload.
104
        LegacyPayload bool
105
}
106

107
// PackHopPayload writes to the passed io.Writer, the series of byes that can
108
// be placed directly into the per-hop payload (EOB) for this hop. This will
109
// include the required routing fields, as well as serializing any of the
110
// passed optional TLVRecords.  nextChanID is the unique channel ID that
111
// references the _outgoing_ channel ID that follows this hop. This field
112
// follows the same semantics as the NextAddress field in the onion: it should
113
// be set to zero to indicate the terminal hop.
114
func (h *Hop) PackHopPayload(w io.Writer, nextChanID uint64) error {
×
115
        // If this is a legacy payload, then we'll exit here as this method
×
116
        // shouldn't be called.
×
117
        if h.LegacyPayload == true {
×
118
                return fmt.Errorf("cannot pack hop payloads for legacy " +
×
119
                        "payloads")
×
120
        }
×
121

122
        // Otherwise, we'll need to make a new stream that includes our
123
        // required routing fields, as well as these optional values.
124
        var records []tlv.Record
×
125

×
126
        // Every hop must have an amount to forward and CLTV expiry.
×
127
        amt := uint64(h.AmtToForward)
×
128
        records = append(records,
×
129
                record.NewAmtToFwdRecord(&amt),
×
130
                record.NewLockTimeRecord(&h.OutgoingTimeLock),
×
131
        )
×
132

×
133
        // BOLT 04 says the next_hop_id should be omitted for the final hop,
×
134
        // but present for all others.
×
135
        //
×
136
        // TODO(conner): test using hop.Exit once available
×
137
        if nextChanID != 0 {
×
138
                records = append(records,
×
139
                        record.NewNextHopIDRecord(&nextChanID),
×
140
                )
×
141
        }
×
142

143
        // Append any custom types destined for this hop.
144
        records = append(records, h.TLVRecords...)
×
145

×
146
        // To ensure we produce a canonical stream, we'll sort the records
×
147
        // before encoding them as a stream in the hop payload.
×
148
        tlv.SortRecords(records)
×
149

×
150
        tlvStream, err := tlv.NewStream(records...)
×
151
        if err != nil {
×
152
                return err
×
153
        }
×
154

155
        return tlvStream.Encode(w)
×
156
}
157

158
// Route represents a path through the channel graph which runs over one or
159
// more channels in succession. This struct carries all the information
160
// required to craft the Sphinx onion packet, and send the payment along the
161
// first hop in the path. A route is only selected as valid if all the channels
162
// have sufficient capacity to carry the initial payment amount after fees are
163
// accounted for.
164
type Route struct {
165
        // TotalTimeLock is the cumulative (final) time lock across the entire
166
        // route. This is the CLTV value that should be extended to the first
167
        // hop in the route. All other hops will decrement the time-lock as
168
        // advertised, leaving enough time for all hops to wait for or present
169
        // the payment preimage to complete the payment.
170
        TotalTimeLock uint32
171

172
        // TotalAmount is the total amount of funds required to complete a
173
        // payment over this route. This value includes the cumulative fees at
174
        // each hop. As a result, the HTLC extended to the first-hop in the
175
        // route will need to have at least this many satoshis, otherwise the
176
        // route will fail at an intermediate node due to an insufficient
177
        // amount of fees.
178
        TotalAmount lnwire.MilliSatoshi
179

180
        // SourcePubKey is the pubkey of the node where this route originates
181
        // from.
182
        SourcePubKey Vertex
183

184
        // Hops contains details concerning the specific forwarding details at
185
        // each hop.
186
        Hops []*Hop
187
}
188

189
// HopFee returns the fee charged by the route hop indicated by hopIndex.
190
func (r *Route) HopFee(hopIndex int) lnwire.MilliSatoshi {
×
191
        var incomingAmt lnwire.MilliSatoshi
×
192
        if hopIndex == 0 {
×
193
                incomingAmt = r.TotalAmount
×
194
        } else {
×
195
                incomingAmt = r.Hops[hopIndex-1].AmtToForward
×
196
        }
×
197

198
        // Fee is calculated as difference between incoming and outgoing amount.
199
        return incomingAmt - r.Hops[hopIndex].AmtToForward
×
200
}
201

202
// TotalFees is the sum of the fees paid at each hop within the final route. In
203
// the case of a one-hop payment, this value will be zero as we don't need to
204
// pay a fee to ourself.
205
func (r *Route) TotalFees() lnwire.MilliSatoshi {
×
206
        if len(r.Hops) == 0 {
×
207
                return 0
×
208
        }
×
209

210
        return r.TotalAmount - r.Hops[len(r.Hops)-1].AmtToForward
×
211
}
212

213
// NewRouteFromHops creates a new Route structure from the minimally required
214
// information to perform the payment. It infers fee amounts and populates the
215
// node, chan and prev/next hop maps.
216
func NewRouteFromHops(amtToSend lnwire.MilliSatoshi, timeLock uint32,
217
        sourceVertex Vertex, hops []*Hop) (*Route, error) {
×
218

×
219
        if len(hops) == 0 {
×
220
                return nil, ErrNoRouteHopsProvided
×
221
        }
×
222

223
        // First, we'll create a route struct and populate it with the fields
224
        // for which the values are provided as arguments of this function.
225
        // TotalFees is determined based on the difference between the amount
226
        // that is send from the source and the final amount that is received
227
        // by the destination.
228
        route := &Route{
×
229
                SourcePubKey:  sourceVertex,
×
230
                Hops:          hops,
×
231
                TotalTimeLock: timeLock,
×
232
                TotalAmount:   amtToSend,
×
233
        }
×
234

×
235
        return route, nil
×
236
}
237

238
// ToSphinxPath converts a complete route into a sphinx PaymentPath that
239
// contains the per-hop paylods used to encoding the HTLC routing data for each
240
// hop in the route. This method also accepts an optional EOB payload for the
241
// final hop.
242
func (r *Route) ToSphinxPath() (*sphinx.PaymentPath, error) {
×
243
        var path sphinx.PaymentPath
×
244

×
245
        // For each hop encoded within the route, we'll convert the hop struct
×
246
        // to an OnionHop with matching per-hop payload within the path as used
×
247
        // by the sphinx package.
×
248
        for i, hop := range r.Hops {
×
249
                pub, err := btcec.ParsePubKey(hop.PubKeyBytes[:])
×
250
                if err != nil {
×
251
                        return nil, err
×
252
                }
×
253

254
                // As a base case, the next hop is set to all zeroes in order
255
                // to indicate that the "last hop" as no further hops after it.
256
                nextHop := uint64(0)
×
257

×
258
                // If we aren't on the last hop, then we set the "next address"
×
259
                // field to be the channel that directly follows it.
×
260
                if i != len(r.Hops)-1 {
×
261
                        nextHop = r.Hops[i+1].ChannelID
×
262
                }
×
263

264
                var payload sphinx.HopPayload
×
265

×
266
                // If this is the legacy payload, then we can just include the
×
267
                // hop data as normal.
×
268
                if hop.LegacyPayload {
×
269
                        // Before we encode this value, we'll pack the next hop
×
270
                        // into the NextAddress field of the hop info to ensure
×
271
                        // we point to the right now.
×
272
                        hopData := sphinx.HopData{
×
273
                                ForwardAmount: uint64(hop.AmtToForward),
×
274
                                OutgoingCltv:  hop.OutgoingTimeLock,
×
275
                        }
×
276
                        binary.BigEndian.PutUint64(
×
277
                                hopData.NextAddress[:], nextHop,
×
278
                        )
×
279

×
280
                        payload, err = sphinx.NewLegacyHopPayload(&hopData)
×
281
                        if err != nil {
×
282
                                return nil, err
×
283
                        }
×
284
                } else {
×
285
                        // For non-legacy payloads, we'll need to pack the
×
286
                        // routing information, along with any extra TLV
×
287
                        // information into the new per-hop payload format.
×
288
                        // We'll also pass in the chan ID of the hop this
×
289
                        // channel should be forwarded to so we can construct a
×
290
                        // valid payload.
×
291
                        var b bytes.Buffer
×
292
                        err := hop.PackHopPayload(&b, nextHop)
×
293
                        if err != nil {
×
294
                                return nil, err
×
295
                        }
×
296

297
                        payload, err = sphinx.NewTLVHopPayload(b.Bytes())
×
298
                        if err != nil {
×
299
                                return nil, err
×
300
                        }
×
301
                }
302

303
                path[i] = sphinx.OnionHop{
×
304
                        NodePub:    *pub,
×
305
                        HopPayload: payload,
×
306
                }
×
307
        }
308

309
        return &path, nil
×
310
}
311

312
// String returns a human readable representation of the route.
313
func (r *Route) String() string {
×
314
        var b strings.Builder
×
315

×
316
        for i, hop := range r.Hops {
×
317
                if i > 0 {
×
318
                        b.WriteString(",")
×
319
                }
×
320
                b.WriteString(strconv.FormatUint(hop.ChannelID, 10))
×
321
        }
322

323
        return fmt.Sprintf("amt=%v, fees=%v, tl=%v, chans=%v",
×
324
                r.TotalAmount-r.TotalFees(), r.TotalFees(), r.TotalTimeLock,
×
325
                b.String(),
×
326
        )
×
327
}
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