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

lightningnetwork / lnd / 13211764208

08 Feb 2025 03:08AM UTC coverage: 49.288% (-9.5%) from 58.815%
13211764208

Pull #9489

github

calvinrzachman
itest: verify switchrpc server enforces send then track

We prevent the rpc server from allowing onion dispatches for
attempt IDs which have already been tracked by rpc clients.

This helps protect the client from leaking a duplicate onion
attempt. NOTE: This is not the only method for solving this
issue! The issue could be addressed via careful client side
programming which accounts for the uncertainty and async
nature of dispatching onions to a remote process via RPC.
This would require some lnd ChannelRouter changes for how
we intend to use these RPCs though.
Pull Request #9489: multi: add BuildOnion, SendOnion, and TrackOnion RPCs

474 of 990 new or added lines in 11 files covered. (47.88%)

27321 existing lines in 435 files now uncovered.

101192 of 205306 relevant lines covered (49.29%)

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/migration_10_route_tlv_records.go
1
package migration_01_to_11
2

3
import (
4
        "bytes"
5
        "io"
6

7
        "github.com/lightningnetwork/lnd/kvdb"
8
)
9

10
// MigrateRouteSerialization migrates the way we serialize routes across the
11
// entire database. At the time of writing of this migration, this includes our
12
// payment attempts, as well as the payment results in mission control.
UNCOV
13
func MigrateRouteSerialization(tx kvdb.RwTx) error {
×
UNCOV
14
        // First, we'll do all the payment attempts.
×
UNCOV
15
        rootPaymentBucket := tx.ReadWriteBucket(paymentsRootBucket)
×
UNCOV
16
        if rootPaymentBucket == nil {
×
17
                return nil
×
18
        }
×
19

20
        // As we can't mutate a bucket while we're iterating over it with
21
        // ForEach, we'll need to collect all the known payment hashes in
22
        // memory first.
UNCOV
23
        var payHashes [][]byte
×
UNCOV
24
        err := rootPaymentBucket.ForEach(func(k, v []byte) error {
×
UNCOV
25
                if v != nil {
×
26
                        return nil
×
27
                }
×
28

UNCOV
29
                payHashes = append(payHashes, k)
×
UNCOV
30
                return nil
×
31
        })
UNCOV
32
        if err != nil {
×
33
                return err
×
34
        }
×
35

36
        // Now that we have all the payment hashes, we can carry out the
37
        // migration itself.
UNCOV
38
        for _, payHash := range payHashes {
×
UNCOV
39
                payHashBucket := rootPaymentBucket.NestedReadWriteBucket(payHash)
×
UNCOV
40

×
UNCOV
41
                // First, we'll migrate the main (non duplicate) payment to
×
UNCOV
42
                // this hash.
×
UNCOV
43
                err := migrateAttemptEncoding(tx, payHashBucket)
×
UNCOV
44
                if err != nil {
×
45
                        return err
×
46
                }
×
47

48
                // Now that we've migrated the main payment, we'll also check
49
                // for any duplicate payments to the same payment hash.
UNCOV
50
                dupBucket := payHashBucket.NestedReadWriteBucket(paymentDuplicateBucket)
×
UNCOV
51

×
UNCOV
52
                // If there's no dup bucket, then we can move on to the next
×
UNCOV
53
                // payment.
×
UNCOV
54
                if dupBucket == nil {
×
UNCOV
55
                        continue
×
56
                }
57

58
                // Otherwise, we'll now iterate through all the duplicate pay
59
                // hashes and migrate those.
UNCOV
60
                var dupSeqNos [][]byte
×
UNCOV
61
                err = dupBucket.ForEach(func(k, v []byte) error {
×
UNCOV
62
                        dupSeqNos = append(dupSeqNos, k)
×
UNCOV
63
                        return nil
×
UNCOV
64
                })
×
UNCOV
65
                if err != nil {
×
66
                        return err
×
67
                }
×
68

69
                // Now in this second pass, we'll re-serialize their duplicate
70
                // payment attempts under the new encoding.
UNCOV
71
                for _, seqNo := range dupSeqNos {
×
UNCOV
72
                        dupPayHashBucket := dupBucket.NestedReadWriteBucket(seqNo)
×
UNCOV
73
                        err := migrateAttemptEncoding(tx, dupPayHashBucket)
×
UNCOV
74
                        if err != nil {
×
75
                                return err
×
76
                        }
×
77
                }
78
        }
79

UNCOV
80
        log.Infof("Migration of route/hop serialization complete!")
×
UNCOV
81

×
UNCOV
82
        log.Infof("Migrating to new mission control store by clearing " +
×
UNCOV
83
                "existing data")
×
UNCOV
84

×
UNCOV
85
        resultsKey := []byte("missioncontrol-results")
×
UNCOV
86
        err = tx.DeleteTopLevelBucket(resultsKey)
×
UNCOV
87
        if err != nil && err != kvdb.ErrBucketNotFound {
×
88
                return err
×
89
        }
×
90

UNCOV
91
        log.Infof("Migration to new mission control completed!")
×
UNCOV
92

×
UNCOV
93
        return nil
×
94
}
95

96
// migrateAttemptEncoding migrates payment attempts using the legacy format to
97
// the new format.
UNCOV
98
func migrateAttemptEncoding(tx kvdb.RwTx, payHashBucket kvdb.RwBucket) error {
×
UNCOV
99
        payAttemptBytes := payHashBucket.Get(paymentAttemptInfoKey)
×
UNCOV
100
        if payAttemptBytes == nil {
×
101
                return nil
×
102
        }
×
103

104
        // For our migration, we'll first read out the existing payment attempt
105
        // using the legacy serialization of the attempt.
UNCOV
106
        payAttemptReader := bytes.NewReader(payAttemptBytes)
×
UNCOV
107
        payAttempt, err := deserializePaymentAttemptInfoLegacy(
×
UNCOV
108
                payAttemptReader,
×
UNCOV
109
        )
×
UNCOV
110
        if err != nil {
×
111
                return err
×
112
        }
×
113

114
        // Now that we have the old attempts, we'll explicitly mark this as
115
        // needing a legacy payload, since after this migration, the modern
116
        // payload will be the default if signalled.
UNCOV
117
        for _, hop := range payAttempt.Route.Hops {
×
UNCOV
118
                hop.LegacyPayload = true
×
UNCOV
119
        }
×
120

121
        // Finally, we'll write out the payment attempt using the new encoding.
UNCOV
122
        var b bytes.Buffer
×
UNCOV
123
        err = serializePaymentAttemptInfo(&b, payAttempt)
×
UNCOV
124
        if err != nil {
×
125
                return err
×
126
        }
×
127

UNCOV
128
        return payHashBucket.Put(paymentAttemptInfoKey, b.Bytes())
×
129
}
130

UNCOV
131
func deserializePaymentAttemptInfoLegacy(r io.Reader) (*PaymentAttemptInfo, error) {
×
UNCOV
132
        a := &PaymentAttemptInfo{}
×
UNCOV
133
        err := ReadElements(r, &a.PaymentID, &a.SessionKey)
×
UNCOV
134
        if err != nil {
×
135
                return nil, err
×
136
        }
×
UNCOV
137
        a.Route, err = deserializeRouteLegacy(r)
×
UNCOV
138
        if err != nil {
×
139
                return nil, err
×
140
        }
×
UNCOV
141
        return a, nil
×
142
}
143

UNCOV
144
func serializePaymentAttemptInfoLegacy(w io.Writer, a *PaymentAttemptInfo) error {
×
UNCOV
145
        if err := WriteElements(w, a.PaymentID, a.SessionKey); err != nil {
×
146
                return err
×
147
        }
×
148

UNCOV
149
        if err := serializeRouteLegacy(w, a.Route); err != nil {
×
150
                return err
×
151
        }
×
152

UNCOV
153
        return nil
×
154
}
155

UNCOV
156
func deserializeHopLegacy(r io.Reader) (*Hop, error) {
×
UNCOV
157
        h := &Hop{}
×
UNCOV
158

×
UNCOV
159
        var pub []byte
×
UNCOV
160
        if err := ReadElements(r, &pub); err != nil {
×
161
                return nil, err
×
162
        }
×
UNCOV
163
        copy(h.PubKeyBytes[:], pub)
×
UNCOV
164

×
UNCOV
165
        if err := ReadElements(r,
×
UNCOV
166
                &h.ChannelID, &h.OutgoingTimeLock, &h.AmtToForward,
×
UNCOV
167
        ); err != nil {
×
168
                return nil, err
×
169
        }
×
170

UNCOV
171
        return h, nil
×
172
}
173

UNCOV
174
func serializeHopLegacy(w io.Writer, h *Hop) error {
×
UNCOV
175
        if err := WriteElements(w,
×
UNCOV
176
                h.PubKeyBytes[:], h.ChannelID, h.OutgoingTimeLock,
×
UNCOV
177
                h.AmtToForward,
×
UNCOV
178
        ); err != nil {
×
179
                return err
×
180
        }
×
181

UNCOV
182
        return nil
×
183
}
184

UNCOV
185
func deserializeRouteLegacy(r io.Reader) (Route, error) {
×
UNCOV
186
        rt := Route{}
×
UNCOV
187
        if err := ReadElements(r,
×
UNCOV
188
                &rt.TotalTimeLock, &rt.TotalAmount,
×
UNCOV
189
        ); err != nil {
×
190
                return rt, err
×
191
        }
×
192

UNCOV
193
        var pub []byte
×
UNCOV
194
        if err := ReadElements(r, &pub); err != nil {
×
195
                return rt, err
×
196
        }
×
UNCOV
197
        copy(rt.SourcePubKey[:], pub)
×
UNCOV
198

×
UNCOV
199
        var numHops uint32
×
UNCOV
200
        if err := ReadElements(r, &numHops); err != nil {
×
201
                return rt, err
×
202
        }
×
203

UNCOV
204
        var hops []*Hop
×
UNCOV
205
        for i := uint32(0); i < numHops; i++ {
×
UNCOV
206
                hop, err := deserializeHopLegacy(r)
×
UNCOV
207
                if err != nil {
×
208
                        return rt, err
×
209
                }
×
UNCOV
210
                hops = append(hops, hop)
×
211
        }
UNCOV
212
        rt.Hops = hops
×
UNCOV
213

×
UNCOV
214
        return rt, nil
×
215
}
216

UNCOV
217
func serializeRouteLegacy(w io.Writer, r Route) error {
×
UNCOV
218
        if err := WriteElements(w,
×
UNCOV
219
                r.TotalTimeLock, r.TotalAmount, r.SourcePubKey[:],
×
UNCOV
220
        ); err != nil {
×
221
                return err
×
222
        }
×
223

UNCOV
224
        if err := WriteElements(w, uint32(len(r.Hops))); err != nil {
×
225
                return err
×
226
        }
×
227

UNCOV
228
        for _, h := range r.Hops {
×
UNCOV
229
                if err := serializeHopLegacy(w, h); err != nil {
×
230
                        return err
×
231
                }
×
232
        }
233

UNCOV
234
        return nil
×
235
}
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