• 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/migration13/migration.go
1
package migration13
2

3
import (
4
        "encoding/binary"
5
        "fmt"
6

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

10
var (
11
        paymentsRootBucket = []byte("payments-root-bucket")
12

13
        // paymentCreationInfoKey is a key used in the payment's sub-bucket to
14
        // store the creation info of the payment.
15
        paymentCreationInfoKey = []byte("payment-creation-info")
16

17
        // paymentFailInfoKey is a key used in the payment's sub-bucket to
18
        // store information about the reason a payment failed.
19
        paymentFailInfoKey = []byte("payment-fail-info")
20

21
        // paymentAttemptInfoKey is a key used in the payment's sub-bucket to
22
        // store the info about the latest attempt that was done for the
23
        // payment in question.
24
        paymentAttemptInfoKey = []byte("payment-attempt-info")
25

26
        // paymentSettleInfoKey is a key used in the payment's sub-bucket to
27
        // store the settle info of the payment.
28
        paymentSettleInfoKey = []byte("payment-settle-info")
29

30
        // paymentHtlcsBucket is a bucket where we'll store the information
31
        // about the HTLCs that were attempted for a payment.
32
        paymentHtlcsBucket = []byte("payment-htlcs-bucket")
33

34
        // htlcAttemptInfoKey is a key used in a HTLC's sub-bucket to store the
35
        // info about the attempt that was done for the HTLC in question.
36
        htlcAttemptInfoKey = []byte("htlc-attempt-info")
37

38
        // htlcSettleInfoKey is a key used in a HTLC's sub-bucket to store the
39
        // settle info, if any.
40
        htlcSettleInfoKey = []byte("htlc-settle-info")
41

42
        // htlcFailInfoKey is a key used in a HTLC's sub-bucket to store
43
        // failure information, if any.
44
        htlcFailInfoKey = []byte("htlc-fail-info")
45

46
        byteOrder = binary.BigEndian
47
)
48

49
// MigrateMPP migrates the payments to a new structure that accommodates for mpp
50
// payments.
UNCOV
51
func MigrateMPP(tx kvdb.RwTx) error {
×
UNCOV
52
        log.Infof("Migrating payments to mpp structure")
×
UNCOV
53

×
UNCOV
54
        // Iterate over all payments and store their indexing keys. This is
×
UNCOV
55
        // needed, because no modifications are allowed inside a Bucket.ForEach
×
UNCOV
56
        // loop.
×
UNCOV
57
        paymentsBucket := tx.ReadWriteBucket(paymentsRootBucket)
×
UNCOV
58
        if paymentsBucket == nil {
×
59
                return nil
×
60
        }
×
61

UNCOV
62
        var paymentKeys [][]byte
×
UNCOV
63
        err := paymentsBucket.ForEach(func(k, v []byte) error {
×
UNCOV
64
                paymentKeys = append(paymentKeys, k)
×
UNCOV
65
                return nil
×
UNCOV
66
        })
×
UNCOV
67
        if err != nil {
×
68
                return err
×
69
        }
×
70

71
        // With all keys retrieved, start the migration.
UNCOV
72
        for _, k := range paymentKeys {
×
UNCOV
73
                bucket := paymentsBucket.NestedReadWriteBucket(k)
×
UNCOV
74

×
UNCOV
75
                // We only expect sub-buckets to be found in
×
UNCOV
76
                // this top-level bucket.
×
UNCOV
77
                if bucket == nil {
×
78
                        return fmt.Errorf("non bucket element in " +
×
79
                                "payments bucket")
×
80
                }
×
81

82
                // Fetch old format creation info.
UNCOV
83
                creationInfo := bucket.Get(paymentCreationInfoKey)
×
UNCOV
84
                if creationInfo == nil {
×
85
                        return fmt.Errorf("creation info not found")
×
86
                }
×
87

88
                // Make a copy because bbolt doesn't allow this value to be
89
                // changed in-place.
UNCOV
90
                newCreationInfo := make([]byte, len(creationInfo))
×
UNCOV
91
                copy(newCreationInfo, creationInfo)
×
UNCOV
92

×
UNCOV
93
                // Convert to nano seconds.
×
UNCOV
94
                timeBytes := newCreationInfo[32+8 : 32+8+8]
×
UNCOV
95
                time := byteOrder.Uint64(timeBytes)
×
UNCOV
96
                timeNs := time * 1000000000
×
UNCOV
97
                byteOrder.PutUint64(timeBytes, timeNs)
×
UNCOV
98

×
UNCOV
99
                // Write back new format creation info.
×
UNCOV
100
                err := bucket.Put(paymentCreationInfoKey, newCreationInfo)
×
UNCOV
101
                if err != nil {
×
102
                        return err
×
103
                }
×
104

105
                // No migration needed if there is no attempt stored.
UNCOV
106
                attemptInfo := bucket.Get(paymentAttemptInfoKey)
×
UNCOV
107
                if attemptInfo == nil {
×
108
                        continue
×
109
                }
110

111
                // Delete attempt info on the payment level.
UNCOV
112
                if err := bucket.Delete(paymentAttemptInfoKey); err != nil {
×
113
                        return err
×
114
                }
×
115

116
                // Save attempt id for later use.
UNCOV
117
                attemptID := attemptInfo[:8]
×
UNCOV
118

×
UNCOV
119
                // Discard attempt id. It will become a bucket key in the new
×
UNCOV
120
                // structure.
×
UNCOV
121
                attemptInfo = attemptInfo[8:]
×
UNCOV
122

×
UNCOV
123
                // Append unknown (zero) attempt time.
×
UNCOV
124
                var zero [8]byte
×
UNCOV
125
                attemptInfo = append(attemptInfo, zero[:]...)
×
UNCOV
126

×
UNCOV
127
                // Create bucket that contains all htlcs.
×
UNCOV
128
                htlcsBucket, err := bucket.CreateBucket(paymentHtlcsBucket)
×
UNCOV
129
                if err != nil {
×
130
                        return err
×
131
                }
×
132

133
                // Create an htlc for this attempt.
UNCOV
134
                htlcBucket, err := htlcsBucket.CreateBucket(attemptID)
×
UNCOV
135
                if err != nil {
×
136
                        return err
×
137
                }
×
138

139
                // Save migrated attempt info.
UNCOV
140
                err = htlcBucket.Put(htlcAttemptInfoKey, attemptInfo)
×
UNCOV
141
                if err != nil {
×
142
                        return err
×
143
                }
×
144

145
                // Migrate settle info.
UNCOV
146
                settleInfo := bucket.Get(paymentSettleInfoKey)
×
UNCOV
147
                if settleInfo != nil {
×
UNCOV
148
                        // Payment-level settle info can be deleted.
×
UNCOV
149
                        err := bucket.Delete(paymentSettleInfoKey)
×
UNCOV
150
                        if err != nil {
×
151
                                return err
×
152
                        }
×
153

154
                        // Append unknown (zero) settle time.
UNCOV
155
                        settleInfo = append(settleInfo, zero[:]...)
×
UNCOV
156

×
UNCOV
157
                        // Save settle info.
×
UNCOV
158
                        err = htlcBucket.Put(htlcSettleInfoKey, settleInfo)
×
UNCOV
159
                        if err != nil {
×
160
                                return err
×
161
                        }
×
162

163
                        // Migration for settled htlc completed.
UNCOV
164
                        continue
×
165
                }
166

167
                // If there is no payment-level failure reason, the payment is
168
                // still in flight and nothing else needs to be migrated.
169
                // Otherwise the payment-level failure reason can remain
170
                // unchanged.
UNCOV
171
                inFlight := bucket.Get(paymentFailInfoKey) == nil
×
UNCOV
172
                if inFlight {
×
UNCOV
173
                        continue
×
174
                }
175

176
                // The htlc failed. Add htlc fail info with reason unknown. We
177
                // don't have access to the original failure reason anymore.
UNCOV
178
                failInfo := []byte{
×
UNCOV
179
                        // Fail time unknown.
×
UNCOV
180
                        0, 0, 0, 0, 0, 0, 0, 0,
×
UNCOV
181

×
UNCOV
182
                        // Zero length wire message.
×
UNCOV
183
                        0,
×
UNCOV
184

×
UNCOV
185
                        // Failure reason unknown.
×
UNCOV
186
                        0,
×
UNCOV
187

×
UNCOV
188
                        // Failure source index zero.
×
UNCOV
189
                        0, 0, 0, 0,
×
UNCOV
190
                }
×
UNCOV
191

×
UNCOV
192
                // Save fail info.
×
UNCOV
193
                err = htlcBucket.Put(htlcFailInfoKey, failInfo)
×
UNCOV
194
                if err != nil {
×
195
                        return err
×
196
                }
×
197
        }
198

UNCOV
199
        log.Infof("Migration of payments to mpp structure complete!")
×
UNCOV
200

×
UNCOV
201
        return nil
×
202
}
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