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

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

7
        "github.com/btcsuite/btcd/wire"
8
        "github.com/lightningnetwork/lnd/kvdb"
9
        "github.com/lightningnetwork/lnd/tlv"
10
)
11

12
var (
13
        // openChanBucket stores all the open channel information.
14
        openChanBucket = []byte("open-chan-bucket")
15

16
        // closedChannelBucket stores all the closed channel information.
17
        closedChannelBucket = []byte("closed-chan-bucket")
18

19
        // outpointBucket is an index mapping outpoints to a tlv
20
        // stream of channel data.
21
        outpointBucket = []byte("outpoint-bucket")
22
)
23

24
const (
25
        // A tlv type definition used to serialize an outpoint's indexStatus for
26
        // use in the outpoint index.
27
        indexStatusType tlv.Type = 0
28
)
29

30
// indexStatus is an enum-like type that describes what state the
31
// outpoint is in. Currently only two possible values.
32
type indexStatus uint8
33

34
const (
35
        // outpointOpen represents an outpoint that is open in the outpoint index.
36
        outpointOpen indexStatus = 0
37

38
        // outpointClosed represents an outpoint that is closed in the outpoint
39
        // index.
40
        outpointClosed indexStatus = 1
41
)
42

43
// MigrateOutpointIndex populates the outpoint index with outpoints that
44
// the node already has. This takes every outpoint in the open channel
45
// bucket and every outpoint in the closed channel bucket and stores them
46
// in this index.
UNCOV
47
func MigrateOutpointIndex(tx kvdb.RwTx) error {
×
UNCOV
48
        log.Info("Migrating to the outpoint index")
×
UNCOV
49

×
UNCOV
50
        // First get the set of open outpoints.
×
UNCOV
51
        openList, err := getOpenOutpoints(tx)
×
UNCOV
52
        if err != nil {
×
53
                return err
×
54
        }
×
55

56
        // Then get the set of closed outpoints.
UNCOV
57
        closedList, err := getClosedOutpoints(tx)
×
UNCOV
58
        if err != nil {
×
59
                return err
×
60
        }
×
61

62
        // Get the outpoint bucket which was created in migration 19.
UNCOV
63
        bucket := tx.ReadWriteBucket(outpointBucket)
×
UNCOV
64

×
UNCOV
65
        // Store the set of open outpoints in the outpoint bucket.
×
UNCOV
66
        if err := putOutpoints(bucket, openList, false); err != nil {
×
67
                return err
×
68
        }
×
69

70
        // Store the set of closed outpoints in the outpoint bucket.
UNCOV
71
        return putOutpoints(bucket, closedList, true)
×
72
}
73

74
// getOpenOutpoints traverses through the openChanBucket and returns the
75
// list of these channels' outpoints.
UNCOV
76
func getOpenOutpoints(tx kvdb.RwTx) ([]*wire.OutPoint, error) {
×
UNCOV
77
        var ops []*wire.OutPoint
×
UNCOV
78

×
UNCOV
79
        openBucket := tx.ReadBucket(openChanBucket)
×
UNCOV
80
        if openBucket == nil {
×
81
                return ops, nil
×
82
        }
×
83

84
        // Iterate through every node and chain bucket to get every open
85
        // outpoint.
86
        //
87
        // The bucket tree:
88
        // openChanBucket -> nodePub -> chainHash -> chanPoint
UNCOV
89
        err := openBucket.ForEach(func(k, v []byte) error {
×
UNCOV
90
                // Ensure that the key is the same size as a pubkey and the
×
UNCOV
91
                // value is nil.
×
UNCOV
92
                if len(k) != 33 || v != nil {
×
93
                        return nil
×
94
                }
×
95

UNCOV
96
                nodeBucket := openBucket.NestedReadBucket(k)
×
UNCOV
97
                if nodeBucket == nil {
×
98
                        return nil
×
99
                }
×
100

UNCOV
101
                return nodeBucket.ForEach(func(k, v []byte) error {
×
UNCOV
102
                        // If there's a value it's not a bucket.
×
UNCOV
103
                        if v != nil {
×
104
                                return nil
×
105
                        }
×
106

UNCOV
107
                        chainBucket := nodeBucket.NestedReadBucket(k)
×
UNCOV
108
                        if chainBucket == nil {
×
109
                                return fmt.Errorf("unable to read "+
×
110
                                        "bucket for chain: %x", k)
×
111
                        }
×
112

UNCOV
113
                        return chainBucket.ForEach(func(k, v []byte) error {
×
UNCOV
114
                                // If there's a value it's not a bucket.
×
UNCOV
115
                                if v != nil {
×
116
                                        return nil
×
117
                                }
×
118

UNCOV
119
                                var op wire.OutPoint
×
UNCOV
120
                                r := bytes.NewReader(k)
×
UNCOV
121
                                if err := readOutpoint(r, &op); err != nil {
×
122
                                        return err
×
123
                                }
×
124

UNCOV
125
                                ops = append(ops, &op)
×
UNCOV
126

×
UNCOV
127
                                return nil
×
128
                        })
129
                })
130
        })
UNCOV
131
        if err != nil {
×
132
                return nil, err
×
133
        }
×
UNCOV
134
        return ops, nil
×
135
}
136

137
// getClosedOutpoints traverses through the closedChanBucket and returns
138
// a list of closed outpoints.
UNCOV
139
func getClosedOutpoints(tx kvdb.RwTx) ([]*wire.OutPoint, error) {
×
UNCOV
140
        var ops []*wire.OutPoint
×
UNCOV
141
        closedBucket := tx.ReadBucket(closedChannelBucket)
×
UNCOV
142
        if closedBucket == nil {
×
143
                return ops, nil
×
144
        }
×
145

146
        // Iterate through every key-value pair to gather all outpoints.
UNCOV
147
        err := closedBucket.ForEach(func(k, v []byte) error {
×
UNCOV
148
                var op wire.OutPoint
×
UNCOV
149
                r := bytes.NewReader(k)
×
UNCOV
150
                if err := readOutpoint(r, &op); err != nil {
×
151
                        return err
×
152
                }
×
153

UNCOV
154
                ops = append(ops, &op)
×
UNCOV
155

×
UNCOV
156
                return nil
×
157
        })
UNCOV
158
        if err != nil {
×
159
                return nil, err
×
160
        }
×
161

UNCOV
162
        return ops, nil
×
163
}
164

165
// putOutpoints puts the set of outpoints into the outpoint bucket.
UNCOV
166
func putOutpoints(bucket kvdb.RwBucket, ops []*wire.OutPoint, isClosed bool) error {
×
UNCOV
167
        status := uint8(outpointOpen)
×
UNCOV
168
        if isClosed {
×
UNCOV
169
                status = uint8(outpointClosed)
×
UNCOV
170
        }
×
171

UNCOV
172
        record := tlv.MakePrimitiveRecord(indexStatusType, &status)
×
UNCOV
173
        stream, err := tlv.NewStream(record)
×
UNCOV
174
        if err != nil {
×
175
                return err
×
176
        }
×
177

UNCOV
178
        var b bytes.Buffer
×
UNCOV
179
        if err := stream.Encode(&b); err != nil {
×
180
                return err
×
181
        }
×
182

183
        // Store the set of outpoints with the encoded tlv stream.
UNCOV
184
        for _, op := range ops {
×
UNCOV
185
                var opBuf bytes.Buffer
×
UNCOV
186
                if err := writeOutpoint(&opBuf, op); err != nil {
×
187
                        return err
×
188
                }
×
189

UNCOV
190
                if err := bucket.Put(opBuf.Bytes(), b.Bytes()); err != nil {
×
191
                        return err
×
192
                }
×
193
        }
194

UNCOV
195
        return nil
×
196
}
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