• 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

81.67
/shachain/element.go
1
package shachain
2

3
import (
4
        "crypto/sha256"
5
        "errors"
6

7
        "github.com/btcsuite/btcd/chaincfg/chainhash"
8
)
9

10
// element represents the entity which contains the hash and index
11
// corresponding to it. An element is the output of the shachain PRF. By
12
// comparing two indexes we're able to mutate the hash in such way to derive
13
// another element.
14
type element struct {
15
        index index
16
        hash  chainhash.Hash
17
}
18

19
// newElementFromStr creates new element from the given hash string.
UNCOV
20
func newElementFromStr(s string, index index) (*element, error) {
×
UNCOV
21
        hash, err := hashFromString(s)
×
UNCOV
22
        if err != nil {
×
23
                return nil, err
×
24
        }
×
25

UNCOV
26
        return &element{
×
UNCOV
27
                index: index,
×
UNCOV
28
                hash:  *hash,
×
UNCOV
29
        }, nil
×
30
}
31

32
// derive computes one shachain element from another by applying a series of
33
// bit flips and hashing operations based on the starting and ending index.
34
func (e *element) derive(toIndex index) (*element, error) {
3✔
35
        fromIndex := e.index
3✔
36

3✔
37
        positions, err := fromIndex.deriveBitTransformations(toIndex)
3✔
38
        if err != nil {
6✔
39
                return nil, err
3✔
40
        }
3✔
41

42
        buf := e.hash.CloneBytes()
3✔
43
        for _, position := range positions {
6✔
44
                // Flip the bit and then hash the current state.
3✔
45
                byteNumber := position / 8
3✔
46
                bitNumber := position % 8
3✔
47

3✔
48
                buf[byteNumber] ^= (1 << bitNumber)
3✔
49

3✔
50
                h := sha256.Sum256(buf)
3✔
51
                buf = h[:]
3✔
52
        }
3✔
53

54
        hash, err := chainhash.NewHash(buf)
3✔
55
        if err != nil {
3✔
56
                return nil, err
×
57
        }
×
58

59
        return &element{
3✔
60
                index: toIndex,
3✔
61
                hash:  *hash,
3✔
62
        }, nil
3✔
63
}
64

65
// isEqual returns true if two elements are identical and false otherwise.
66
func (e *element) isEqual(e2 *element) bool {
3✔
67
        return (e.index == e2.index) &&
3✔
68
                (&e.hash).IsEqual(&e2.hash)
3✔
69
}
3✔
70

71
const (
72
        // maxHeight is used to determine the maximum allowable index and the
73
        // length of the array required to order to derive all previous hashes
74
        // by index. The entries of this array as also known as buckets.
75
        maxHeight uint8 = 48
76

77
        // rootIndex is an index which corresponds to the root hash.
78
        rootIndex index = 0
79
)
80

81
// startIndex is the index of first element in the shachain PRF.
82
var startIndex index = (1 << maxHeight) - 1
83

84
// index is a number which identifies the hash number and serves as a way to
85
// determine the hashing operation required  to derive one hash from another.
86
// index is initialized with the startIndex and decreases down to zero with
87
// successive derivations.
88
type index uint64
89

90
// newIndex is used to create index instance. The inner operations with index
91
// implies that index decreasing from some max number to zero, but for
92
// simplicity and backward compatibility with previous logic it was transformed
93
// to work in opposite way.
94
func newIndex(v uint64) index {
3✔
95
        return startIndex - index(v)
3✔
96
}
3✔
97

98
// deriveBitTransformations function checks that the 'to' index is derivable
99
// from the 'from' index by checking the indexes are prefixes of another. The
100
// bit positions where the zeroes should be changed to ones in order for the
101
// indexes to become the same are returned. This set of bits is needed in order
102
// to derive one hash from another.
103
//
104
// NOTE: The index 'to' is derivable from index 'from' iff index 'from' lies
105
// left and above index 'to' on graph below, for example:
106
// 1. 7(0b111) -> 7
107
// 2. 6(0b110) -> 6,7
108
// 3. 5(0b101) -> 5
109
// 4. 4(0b100) -> 4,5,6,7
110
// 5. 3(0b011) -> 3
111
// 6. 2(0b010) -> 2, 3
112
// 7. 1(0b001) -> 1
113
//
114
//          ^ bucket number
115
//          |
116
//        3 |   x
117
//          |   |
118
//        2 |   |               x
119
//          |   |               |
120
//        1 |   |       x       |       x
121
//          |   |       |       |       |
122
//        0 |   |   x   |   x   |   x   |   x
123
//          |   |   |   |   |   |   |   |   |
124
//          +---|---|---|---|---|---|---|---|---> index
125
//              0   1   2   3   4   5   6   7
126
func (from index) deriveBitTransformations(to index) ([]uint8, error) {
3✔
127
        var positions []uint8
3✔
128

3✔
129
        if from == to {
6✔
130
                return positions, nil
3✔
131
        }
3✔
132

133
        //        + --------------- +
134
        //         | №  | from | to  |
135
        //        + -- + ---- + --- +
136
        //        | 48 |         1  |  1  |
137
        //        | 47 |         0  |  0  | [48-5] - same part of 'from' and 'to'
138
        //        | 46 |   0  |  0  |            indexes which also is called prefix.
139
        //                ....
140
        //        |  5 |         1  |  1  |
141
        //        |  4 |         0  |  1  | <--- position after which indexes becomes
142
        //        |  3 |   0  |  0  |         different, after this position
143
        //        |  2 |   0  |  1  |         bits in 'from' index all should be
144
        //        |  1 |   0  |  0  |         zeros or such indexes considered to be
145
        //        |  0 |   0  |  1  |         not derivable.
146
        //        + -- + ---- + --- +
147
        zeros := countTrailingZeros(from)
3✔
148
        if uint64(from) != getPrefix(to, zeros) {
6✔
149
                return nil, errors.New("prefixes are different - indexes " +
3✔
150
                        "aren't derivable")
3✔
151
        }
3✔
152

153
        // The remaining part of 'to' index represents the positions which we
154
        // will use then in order to derive one element from another.
155
        for position := zeros - 1; ; position-- {
6✔
156
                if getBit(to, position) == 1 {
6✔
157
                        positions = append(positions, position)
3✔
158
                }
3✔
159

160
                if position == 0 {
6✔
161
                        break
3✔
162
                }
163
        }
164

165
        return positions, nil
3✔
166
}
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