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

3
import (
4
        "fmt"
5

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

9
var (
10
        // paymentsRootBucket is the name of the top-level bucket within the
11
        // database that stores all data related to payments.
12
        paymentsRootBucket = []byte("payments-root-bucket")
13

14
        // paymentHtlcsBucket is a bucket where we'll store the information
15
        // about the HTLCs that were attempted for a payment.
16
        paymentHtlcsBucket = []byte("payment-htlcs-bucket")
17

18
        // oldAttemptInfoKey is a key used in a HTLC's sub-bucket to store the
19
        // info about the attempt that was done for the HTLC in question.
20
        oldAttemptInfoKey = []byte("htlc-attempt-info")
21

22
        // oldSettleInfoKey is a key used in a HTLC's sub-bucket to store the
23
        // settle info, if any.
24
        oldSettleInfoKey = []byte("htlc-settle-info")
25

26
        // oldFailInfoKey is a key used in a HTLC's sub-bucket to store
27
        // failure information, if any.
28
        oldFailInfoKey = []byte("htlc-fail-info")
29

30
        // htlcAttemptInfoKey is the key used as the prefix of an HTLC attempt
31
        // to store the info about the attempt that was done for the HTLC in
32
        // question. The HTLC attempt ID is concatenated at the end.
33
        htlcAttemptInfoKey = []byte("ai")
34

35
        // htlcSettleInfoKey is the key used as the prefix of an HTLC attempt
36
        // settle info, if any. The HTLC attempt ID is concatenated at the end.
37
        htlcSettleInfoKey = []byte("si")
38

39
        // htlcFailInfoKey is the key used as the prefix of an HTLC attempt
40
        // failure information, if any.The  HTLC attempt ID is concatenated at
41
        // the end.
42
        htlcFailInfoKey = []byte("fi")
43
)
44

45
// htlcBucketKey creates a composite key from prefix and id where the result is
46
// simply the two concatenated. This is the exact copy from payments.go.
UNCOV
47
func htlcBucketKey(prefix, id []byte) []byte {
×
UNCOV
48
        key := make([]byte, len(prefix)+len(id))
×
UNCOV
49
        copy(key, prefix)
×
UNCOV
50
        copy(key[len(prefix):], id)
×
UNCOV
51
        return key
×
UNCOV
52
}
×
53

54
// MigrateHtlcAttempts will gather all htlc-attempt-info's, htlcs-settle-info's
55
// and htlcs-fail-info's from the attempt ID buckes and re-store them using the
56
// flattened keys to each payment's payment-htlcs-bucket.
UNCOV
57
func MigrateHtlcAttempts(tx kvdb.RwTx) error {
×
UNCOV
58
        payments := tx.ReadWriteBucket(paymentsRootBucket)
×
UNCOV
59
        if payments == nil {
×
60
                return nil
×
61
        }
×
62

63
        // Collect all payment hashes so we can migrate payments one-by-one to
64
        // avoid any bugs bbolt might have when invalidating cursors.
65
        // For 100 million payments, this would need about 3 GiB memory so we
66
        // should hopefully be fine for very large nodes too.
UNCOV
67
        var paymentHashes []string
×
UNCOV
68
        if err := payments.ForEach(func(hash, v []byte) error {
×
UNCOV
69
                // Get the bucket which contains the payment, fail if the key
×
UNCOV
70
                // does not have a bucket.
×
UNCOV
71
                bucket := payments.NestedReadBucket(hash)
×
UNCOV
72
                if bucket == nil {
×
UNCOV
73
                        return fmt.Errorf("key must be a bucket: '%v'",
×
UNCOV
74
                                string(paymentsRootBucket))
×
UNCOV
75
                }
×
76

UNCOV
77
                paymentHashes = append(paymentHashes, string(hash))
×
UNCOV
78
                return nil
×
UNCOV
79
        }); err != nil {
×
UNCOV
80
                return err
×
UNCOV
81
        }
×
82

UNCOV
83
        for _, paymentHash := range paymentHashes {
×
UNCOV
84
                payment := payments.NestedReadWriteBucket([]byte(paymentHash))
×
UNCOV
85
                if payment.Get(paymentHtlcsBucket) != nil {
×
UNCOV
86
                        return fmt.Errorf("key must be a bucket: '%v'",
×
UNCOV
87
                                string(paymentHtlcsBucket))
×
UNCOV
88
                }
×
89

UNCOV
90
                htlcs := payment.NestedReadWriteBucket(paymentHtlcsBucket)
×
UNCOV
91
                if htlcs == nil {
×
UNCOV
92
                        // Nothing to migrate for this payment.
×
UNCOV
93
                        continue
×
94
                }
95

UNCOV
96
                if err := migrateHtlcsBucket(htlcs); err != nil {
×
UNCOV
97
                        return err
×
UNCOV
98
                }
×
99
        }
100

UNCOV
101
        return nil
×
102
}
103

104
// migrateHtlcsBucket is a helper to gather, transform and re-store htlc attempt
105
// key/values.
UNCOV
106
func migrateHtlcsBucket(htlcs kvdb.RwBucket) error {
×
UNCOV
107
        // Collect attempt ids so that we can migrate attempts one-by-one
×
UNCOV
108
        // to avoid any bugs bbolt might have when invalidating cursors.
×
UNCOV
109
        var aids []string
×
UNCOV
110

×
UNCOV
111
        // First we collect all htlc attempt ids.
×
UNCOV
112
        if err := htlcs.ForEach(func(aid, v []byte) error {
×
UNCOV
113
                aids = append(aids, string(aid))
×
UNCOV
114
                return nil
×
UNCOV
115
        }); err != nil {
×
116
                return err
×
117
        }
×
118

119
        // Next we go over these attempts, fetch all data and migrate.
UNCOV
120
        for _, aid := range aids {
×
UNCOV
121
                aidKey := []byte(aid)
×
UNCOV
122
                attempt := htlcs.NestedReadWriteBucket(aidKey)
×
UNCOV
123
                if attempt == nil {
×
UNCOV
124
                        return fmt.Errorf("non bucket element '%v' in '%v' "+
×
UNCOV
125
                                "bucket", aidKey, string(paymentHtlcsBucket))
×
UNCOV
126
                }
×
127

128
                // Collect attempt/settle/fail infos.
UNCOV
129
                attemptInfo := attempt.Get(oldAttemptInfoKey)
×
UNCOV
130
                if len(attemptInfo) > 0 {
×
UNCOV
131
                        newKey := htlcBucketKey(htlcAttemptInfoKey, aidKey)
×
UNCOV
132
                        if err := htlcs.Put(newKey, attemptInfo); err != nil {
×
133
                                return err
×
134
                        }
×
135
                }
136

UNCOV
137
                settleInfo := attempt.Get(oldSettleInfoKey)
×
UNCOV
138
                if len(settleInfo) > 0 {
×
UNCOV
139
                        newKey := htlcBucketKey(htlcSettleInfoKey, aidKey)
×
UNCOV
140
                        if err := htlcs.Put(newKey, settleInfo); err != nil {
×
141
                                return err
×
142
                        }
×
143
                }
144

UNCOV
145
                failInfo := attempt.Get(oldFailInfoKey)
×
UNCOV
146
                if len(failInfo) > 0 {
×
UNCOV
147
                        newKey := htlcBucketKey(htlcFailInfoKey, aidKey)
×
UNCOV
148
                        if err := htlcs.Put(newKey, failInfo); err != nil {
×
149
                                return err
×
150
                        }
×
151
                }
152
        }
153

154
        // Finally we delete old attempt buckets.
UNCOV
155
        for _, aid := range aids {
×
UNCOV
156
                if err := htlcs.DeleteNestedBucket([]byte(aid)); err != nil {
×
157
                        return err
×
158
                }
×
159
        }
160

UNCOV
161
        return nil
×
162
}
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