• 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

59.83
/channeldb/witness_cache.go
1
package channeldb
2

3
import (
4
        "fmt"
5

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

10
var (
11
        // ErrNoWitnesses is an error that's returned when no new witnesses have
12
        // been added to the WitnessCache.
13
        ErrNoWitnesses = fmt.Errorf("no witnesses")
14

15
        // ErrUnknownWitnessType is returned if a caller attempts to
16
        ErrUnknownWitnessType = fmt.Errorf("unknown witness type")
17
)
18

19
// WitnessType is enum that denotes what "type" of witness is being
20
// stored/retrieved. As the WitnessCache itself is agnostic and doesn't enforce
21
// any structure on added witnesses, we use this type to partition the
22
// witnesses on disk, and also to know how to map a witness to its look up key.
23
type WitnessType uint8
24

25
var (
26
        // Sha256HashWitness is a witness that is simply the pre image to a
27
        // hash image. In order to map to its key, we'll use sha256.
28
        Sha256HashWitness WitnessType = 1
29
)
30

31
// toDBKey is a helper method that maps a witness type to the key that we'll
32
// use to store it within the database.
33
func (w WitnessType) toDBKey() ([]byte, error) {
3✔
34
        switch w {
3✔
35

36
        case Sha256HashWitness:
3✔
37
                return []byte{byte(w)}, nil
3✔
38

39
        default:
×
40
                return nil, ErrUnknownWitnessType
×
41
        }
42
}
43

44
var (
45
        // witnessBucketKey is the name of the bucket that we use to store all
46
        // witnesses encountered. Within this bucket, we'll create a sub-bucket for
47
        // each witness type.
48
        witnessBucketKey = []byte("byte")
49
)
50

51
// WitnessCache is a persistent cache of all witnesses we've encountered on the
52
// network. In the case of multi-hop, multi-step contracts, a cache of all
53
// witnesses can be useful in the case of partial contract resolution. If
54
// negotiations break down, we may be forced to locate the witness for a
55
// portion of the contract on-chain. In this case, we'll then add that witness
56
// to the cache so the incoming contract can fully resolve witness.
57
// Additionally, as one MUST always use a unique witness on the network, we may
58
// use this cache to detect duplicate witnesses.
59
//
60
// TODO(roasbeef): need expiry policy?
61
//   - encrypt?
62
type WitnessCache struct {
63
        db *DB
64
}
65

66
// NewWitnessCache returns a new instance of the witness cache.
67
func (d *DB) NewWitnessCache() *WitnessCache {
3✔
68
        return &WitnessCache{
3✔
69
                db: d,
3✔
70
        }
3✔
71
}
3✔
72

73
// witnessEntry is a key-value struct that holds each key -> witness pair, used
74
// when inserting records into the cache.
75
type witnessEntry struct {
76
        key     []byte
77
        witness []byte
78
}
79

80
// AddSha256Witnesses adds a batch of new sha256 preimages into the witness
81
// cache. This is an alias for AddWitnesses that uses Sha256HashWitness as the
82
// preimages' witness type.
83
func (w *WitnessCache) AddSha256Witnesses(preimages ...lntypes.Preimage) error {
3✔
84
        // Optimistically compute the preimages' hashes before attempting to
3✔
85
        // start the db transaction.
3✔
86
        entries := make([]witnessEntry, 0, len(preimages))
3✔
87
        for i := range preimages {
6✔
88
                hash := preimages[i].Hash()
3✔
89
                entries = append(entries, witnessEntry{
3✔
90
                        key:     hash[:],
3✔
91
                        witness: preimages[i][:],
3✔
92
                })
3✔
93
        }
3✔
94

95
        return w.addWitnessEntries(Sha256HashWitness, entries)
3✔
96
}
97

98
// addWitnessEntries inserts the witnessEntry key-value pairs into the cache,
99
// using the appropriate witness type to segment the namespace of possible
100
// witness types.
101
func (w *WitnessCache) addWitnessEntries(wType WitnessType,
102
        entries []witnessEntry) error {
3✔
103

3✔
104
        // Exit early if there are no witnesses to add.
3✔
105
        if len(entries) == 0 {
3✔
106
                return nil
×
107
        }
×
108

109
        return kvdb.Batch(w.db.Backend, func(tx kvdb.RwTx) error {
6✔
110
                witnessBucket, err := tx.CreateTopLevelBucket(witnessBucketKey)
3✔
111
                if err != nil {
3✔
112
                        return err
×
113
                }
×
114

115
                witnessTypeBucketKey, err := wType.toDBKey()
3✔
116
                if err != nil {
3✔
117
                        return err
×
118
                }
×
119
                witnessTypeBucket, err := witnessBucket.CreateBucketIfNotExists(
3✔
120
                        witnessTypeBucketKey,
3✔
121
                )
3✔
122
                if err != nil {
3✔
123
                        return err
×
124
                }
×
125

126
                for _, entry := range entries {
6✔
127
                        err = witnessTypeBucket.Put(entry.key, entry.witness)
3✔
128
                        if err != nil {
3✔
129
                                return err
×
130
                        }
×
131
                }
132

133
                return nil
3✔
134
        })
135
}
136

137
// LookupSha256Witness attempts to lookup the preimage for a sha256 hash. If
138
// the witness isn't found, ErrNoWitnesses will be returned.
139
func (w *WitnessCache) LookupSha256Witness(hash lntypes.Hash) (lntypes.Preimage, error) {
3✔
140
        witness, err := w.lookupWitness(Sha256HashWitness, hash[:])
3✔
141
        if err != nil {
6✔
142
                return lntypes.Preimage{}, err
3✔
143
        }
3✔
144

145
        return lntypes.MakePreimage(witness)
3✔
146
}
147

148
// lookupWitness attempts to lookup a witness according to its type and also
149
// its witness key. In the case that the witness isn't found, ErrNoWitnesses
150
// will be returned.
151
func (w *WitnessCache) lookupWitness(wType WitnessType, witnessKey []byte) ([]byte, error) {
3✔
152
        var witness []byte
3✔
153
        err := kvdb.View(w.db, func(tx kvdb.RTx) error {
6✔
154
                witnessBucket := tx.ReadBucket(witnessBucketKey)
3✔
155
                if witnessBucket == nil {
6✔
156
                        return ErrNoWitnesses
3✔
157
                }
3✔
158

159
                witnessTypeBucketKey, err := wType.toDBKey()
3✔
160
                if err != nil {
3✔
161
                        return err
×
162
                }
×
163
                witnessTypeBucket := witnessBucket.NestedReadBucket(witnessTypeBucketKey)
3✔
164
                if witnessTypeBucket == nil {
3✔
UNCOV
165
                        return ErrNoWitnesses
×
UNCOV
166
                }
×
167

168
                dbWitness := witnessTypeBucket.Get(witnessKey)
3✔
169
                if dbWitness == nil {
6✔
170
                        return ErrNoWitnesses
3✔
171
                }
3✔
172

173
                witness = make([]byte, len(dbWitness))
3✔
174
                copy(witness[:], dbWitness)
3✔
175

3✔
176
                return nil
3✔
177
        }, func() {
3✔
178
                witness = nil
3✔
179
        })
3✔
180
        if err != nil {
6✔
181
                return nil, err
3✔
182
        }
3✔
183

184
        return witness, nil
3✔
185
}
186

187
// DeleteSha256Witness attempts to delete a sha256 preimage identified by hash.
UNCOV
188
func (w *WitnessCache) DeleteSha256Witness(hash lntypes.Hash) error {
×
UNCOV
189
        return w.deleteWitness(Sha256HashWitness, hash[:])
×
UNCOV
190
}
×
191

192
// deleteWitness attempts to delete a particular witness from the database.
UNCOV
193
func (w *WitnessCache) deleteWitness(wType WitnessType, witnessKey []byte) error {
×
UNCOV
194
        return kvdb.Batch(w.db.Backend, func(tx kvdb.RwTx) error {
×
UNCOV
195
                witnessBucket, err := tx.CreateTopLevelBucket(witnessBucketKey)
×
UNCOV
196
                if err != nil {
×
197
                        return err
×
198
                }
×
199

UNCOV
200
                witnessTypeBucketKey, err := wType.toDBKey()
×
UNCOV
201
                if err != nil {
×
202
                        return err
×
203
                }
×
UNCOV
204
                witnessTypeBucket, err := witnessBucket.CreateBucketIfNotExists(
×
UNCOV
205
                        witnessTypeBucketKey,
×
UNCOV
206
                )
×
UNCOV
207
                if err != nil {
×
208
                        return err
×
209
                }
×
210

UNCOV
211
                return witnessTypeBucket.Delete(witnessKey)
×
212
        })
213
}
214

215
// DeleteWitnessClass attempts to delete an *entire* class of witnesses. After
216
// this function return with a non-nil error,
UNCOV
217
func (w *WitnessCache) DeleteWitnessClass(wType WitnessType) error {
×
UNCOV
218
        return kvdb.Batch(w.db.Backend, func(tx kvdb.RwTx) error {
×
UNCOV
219
                witnessBucket, err := tx.CreateTopLevelBucket(witnessBucketKey)
×
UNCOV
220
                if err != nil {
×
221
                        return err
×
222
                }
×
223

UNCOV
224
                witnessTypeBucketKey, err := wType.toDBKey()
×
UNCOV
225
                if err != nil {
×
226
                        return err
×
227
                }
×
228

UNCOV
229
                return witnessBucket.DeleteNestedBucket(witnessTypeBucketKey)
×
230
        })
231
}
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